좋아하는 (영리한) 방어 프로그래밍 모범 사례 [닫기]


148

방어 코딩을 위해 좋아하는 (영리한) 기술 을 선택해야한다면 그 기술은 무엇입니까? 현재 사용하는 언어는 Java 및 Objective-C (C ++의 배경 지식)이지만 모든 언어로 자유롭게 대답 할 수 있습니다. 여기서 우리 중 70 % 이상이 이미 알고있는 것 이외의 영리한 방어 기술 에 중점 을 둘 것입니다. 이제 트릭을 깊이 파고들 차례입니다.

다시 말해이 흥미없는 예 이외의 다른 것을 생각해보십시오 .

  • if(5 == x) 대신 if(x == 5) : 의도하지 않은 할당을 피하기 위해

다음은 흥미로운 방어 적 프로그래밍 방법에 대한 몇 가지 예입니다 (언어 별 예는 Java로되어 있음).

-변수를 변경해야 할 때까지 변수를 잠그십시오.

즉, 변수 를 변경해야한다는 것을 알 때까지 모든 변수를 선언 할 수 있으며이 final시점에서을 제거 할 수 있습니다 final. 일반적으로 알려지지 않은 사실은 이것이 메소드 매개 변수에도 유효하다는 것입니다.

public void foo(final int arg) { /* Stuff Here */ }

-나쁜 일이 생길 때는 증거를 남기십시오

예외가있을 때 수행 할 수있는 여러 가지 작업이 있습니다. 명백하게 기록하고 정리를 수행하는 것은 몇 가지 일 것입니다. 그러나 증거의 흔적을 남길 수도 있습니다 (예 : "UNABLE TO LOAD FILE"또는 99999와 같은 센티넬 값으로 변수를 설정하면 예외 catch블록을 넘어서 버릴 경우 디버거에서 유용 할 수 있습니다 ).

-일관성에 관해서 : 악마는 세부 사항에 있습니다.

사용중인 다른 라이브러리와 일관성을 유지하십시오. 예를 들어 Java에서 값 범위를 추출하는 메소드를 작성하는 경우 하한을 포함 하고 상한을 독점으로 설정하십시오 . 이것은 String.substring(start, end)같은 방식으로 작동하는 것과 같은 방법과 일치하게합니다 . Sun JDK에서 이러한 유형의 모든 메소드는 배열이 일치하는 요소의 반복을 포함하여 다양한 작업을 수행하므로 인덱스가 0 ( 포함 )부터 배열의 길이 ( 독점 ) 까지 다양한 방식으로 작동하므로 이러한 방식으로 작동합니다 .

그렇다면 가장 좋아하는 수비 관행은 무엇입니까?

업데이트 : 아직하지 않은 경우 자유롭게 차임하십시오. 공식 답변을 선택하기 전에 더 많은 답변을 드리겠습니다.


간단한 기술을 모르는 우리의 무식한 30 %를 대표하고 싶습니다 . 모두가 기초로 알아야 할 "명백한"기술에 대한 링크가 있습니까?
elliot42


왜 공식 답변을 선택 하시겠습니까? 그것은 전적으로 엉뚱한 소리입니다.
bzlm

글쎄, 프로그래밍에 관해서는, 영리한 것조차도 "최소한의 놀라움의 법칙"을 기리기 위해 뒷자리를 차지해야합니다. 가장 좋은 답변을 표시하는 스택 오버플로 정신을 깨 뜨리려면 스택 오버플로 프로토콜을 위반합니다 (따라서 규칙을 위반 함). :-) 폐쇄와 같은 I : 그 외에
라이언 Delucchi

답변:


103

c ++에서는 펜스 포스트 오류를 ​​포착하기 위해 여분의 메모리를 제공하기 위해 새로운 재정의를 한 번 좋아했습니다.

현재 저는 테스트 주도 개발 (Test Driven Development)을 선호하는 방어 적 프로그래밍을 피하는 것을 선호합니다 . 오류를 신속하고 외부 적으로 포착하는 경우 방어적인 조작으로 코드를 어지럽 힐 필요가 없으며 코드가 건조 되어 방어해야 할 오류가 줄어 듭니다.

WikiKnowledge가 쓴대로 :

방어 프로그래밍을 피하고 대신 빨리 실패하십시오.

방어 적 프로그래밍이란 데이터의 일부 실패를 보상하는 코드 작성, 호출자가 호출자와 서브 루틴 간의 계약을 준수하지 않는 데이터를 제공 할 수 있고 서브 루틴이 어떻게 든 대처해야한다고 가정하는 코드 작성 습관을 의미합니다. 그것으로.


8
방어 프로그래밍은 프로그램의 다른 부분에 의해 도입 된 불법 조건을 처리하려고합니다. 부적절한 사용자 입력을 처리하는 것은 완전히 다릅니다.
Joe Soul-bringer

5
Errr ... 방어 프로그래밍에 대한이 정의는 문제에 내재적으로 사용 된 정의와도 비슷하지 않습니다.
Sol

15
방어 프로그래밍을 피하십시오. 문제는 데이터의 장애를 "보상"하는 것이 아니라 코드가 의도하지 않은 작업을 수행하도록 설계된 악성 데이터로부터 자신을 보호하는 것입니다. 버퍼 오버 플로우, SQL 인젝션을 참조하십시오. XSS에서 웹 페이지보다 더 빨리 실패하는 것은 아니지만 예쁘지 않습니다
Jorge Córdoba

6
"빠른 실패"절차는 방어 프로그래밍의 한 형태라고 주장합니다.
Ryan Delucchi

6
@ryan은 정확히 맞습니다. 실패는 좋은 방어 개념입니다. 당신이있는 상태가 불가능하다면, 계속 실패하고 실패하지 마십시오! 메타 데이터 중심이라면 더욱 중요합니다. 방어 프로그래밍은 단순히 매개 변수를 확인하는 것이 아닙니다.
Bill K

75

SQL

데이터를 삭제해야 할 때

select *    
--delete    
From mytable    
Where ...

내가 그것을 실행할 때, where 절을 잊어 버렸는지 알 수 있습니다. 안전이 있습니다. 모든 것이 괜찮다면 '-'주석 토큰 다음에있는 모든 것을 강조 표시하고 실행합니다.

편집 : 많은 데이터를 삭제하는 경우 * 대신 count (*)를 사용합니다.


나는 텍스트를 선택할 수없는 내 iPhone에 이것을 썼습니다. 누군가 내 코드를 코드로 표시 할 수 있다면 정말 감사하겠습니다. 감사.
John MacIntyre

6
난 그냥 거래에 싸서 내가 망칠 경우 롤백 수 있습니다 ...
rmeador

36
거래 시작 | 롤백 거래
Dalin Seivewright

3
+1 예, 이것이 거래로 대체 될 수 있다고 생각하지만, 이런 식으로하는 것이 간단합니다.
Ryan Delucchi

3
트랜잭션을 사용하는 것이 좋습니다. 그것은 당신이 그것을 올바르게하고 커밋 할 수있을 때까지 수십 번 이상 실행하고 실제 효과를 볼 수 있음을 의미합니다 .
Ryan Lundy

48

응용 프로그램이 시작될 때 합리적인 메모리 덩어리를 할당하십시오. Steve McConnell은 이것을 Code Complete 의 메모리 낙하산 이라고 부릅니다 .

심각한 문제가 발생하여 종료해야하는 경우에 사용할 수 있습니다.

이 메모리를 미리 할당하면 여유 공간을 확보 한 후 사용 가능한 메모리를 사용하여 다음을 수행 할 수 있으므로 안전망이 제공됩니다.

  • 모든 영구 데이터 저장
  • 적절한 파일을 모두 닫습니다
  • 로그 파일에 오류 메시지 쓰기
  • 사용자에게 의미있는 오류를 제시

나는 이것을 비오는 날 기금이라고 불렀습니다.
plinth

42

기본 사례가없는 모든 switch 문에서 오류 메시지와 함께 프로그램을 중단하는 사례를 추가합니다.

#define INVALID_SWITCH_VALUE 0

switch (x) {
case 1:
  // ...
  break;
case 2:
  // ...
  break;
case 3:
  // ...
  break;
default:
  assert(INVALID_SWITCH_VALUE);
}

2
아니면 현대적인 언어로 던질 수도 있습니다.
Tom Hawtin-tackline

2
Assert는 컴파일 타임에 그 효과를 전역 적으로 비활성화 할 수 있다는 이점이 있습니다. 그러나 어떤 상황에서는 언어가 지원하는 경우 던지기가 더 적합 할 수 있습니다.
Diomidis Spinellis

2
Assert는 프로덕션 코드에 유용한 또 다른 이점이 있습니다. 문제가 발생하면 정확히 어떤 오류가 발생했는지와 오류가 발생한 프로그램 줄을 알려줍니다. 그런 버그 리포트는 훌륭합니다!
메이슨 휠러

2
@Diomidis : 또 다른 각도는 다음과 같습니다. Assert는 컴파일 타임에 그 효과를 전역 적으로 비활성화 할 수 있다는 단점 이 있습니다.
Disillusioned

1
던지는 것이 더 좋습니다. 그런 다음 테스트 범위가 적절한 지 확인하십시오.
Dominic Cronin

41

열거 형 (C #)의 다양한 상태를 처리 할 때 :

enum AccountType
{
    Savings,
    Checking,
    MoneyMarket
}

그런 다음, 일상 안에서 ...

switch (accountType)
{
    case AccountType.Checking:
        // do something

    case AccountType.Savings:
        // do something else

    case AccountType.MoneyMarket:
        // do some other thing

    default:
-->     Debug.Fail("Invalid account type.");
}

언젠가이 열거 형에 다른 계정 유형을 추가하겠습니다. 그리고 내가 할 때, 나는이 스위치 문장을 고치는 것을 잊을 것이다. 따라서이 Debug.Fail사실에주의를 끌기 위해 디버그 모드에서 끔찍하게 충돌이 발생합니다. 을 추가하면 case AccountType.MyNewAccountType:다른 계정 유형을 추가하고 여기에서 사례를 업데이트하는 것을 잊을 때까지 끔찍한 충돌이 중지됩니다.

(예, 다형성이 아마도 더 좋을 수도 있지만 이것은 단지 내 머리 꼭대기의 예일뿐입니다.)


4
대부분의 컴파일러는 case 블록에서 일부 열거 형을 처리하지 않으면 경고를 발행 할만 큼 똑똑합니다. 그러나 기본값을 실패로 설정하는 것은 여전히 ​​좋은 형태입니다. 열거 형은 숫자 일뿐이며 메모리 손상이 발생하면 잘못된 값이 나타날 수 있습니다.
Adam Hawes

c에서는 끝에 마지막에 invalidAccountType을 추가하는 데 사용했습니다. 이것은 때때로 유용합니다.
Ray Tayek

1
@Adam- 모든 것을 다시 컴파일하면 컴파일러에서 경고를 표시합니다 . 새 클래스를 추가하고 부분적으로 만 다시 컴파일하면 위와 같은 내용이 표시되지 않을 수 있으며 기본 경우에 저장됩니다. 조용히 실패하지는 않습니다.
Eddie

4
코멘트에 슬래시가 포함되어 있습니다. : P
Ryan Lundy

2
디버그 후에는 프로덕션 코드에 대해 'throw new NotSupportedException ()'이어야합니다.
user7116

35

문자열 (특히 사용자 입력에 따라 다름)과 함께 오류 메시지를 인쇄 할 때 항상 작은 따옴표를 사용합니다 ''. 예를 들면 다음과 같습니다.

FILE *fp = fopen(filename, "r");
if(fp == NULL) {
    fprintf(stderr, "ERROR: Could not open file %s\n", filename);
    return false;
}

%s파일 이름이 빈 문자열이거나 공백 또는 무언가이기 때문에 따옴표 가 부족합니다 . 인쇄 된 메시지는 물론 다음과 같습니다.

ERROR: Could not open file

따라서 항상하는 것이 좋습니다.

fprintf(stderr, "ERROR: Could not open file '%s'\n", filename);

그런 다음 최소한 사용자에게 다음이 표시됩니다.

ERROR: Could not open file ''

최종 사용자가 제출 한 버그 보고서의 품질 측면에서 큰 차이가 있습니다. 일반적인 소리가 아닌 이와 같은 재미있는 오류 메시지가있는 경우 "내 파일을 열지 않습니다"라고 쓰지 않고 복사 / 붙여 넣기 할 가능성이 훨씬 높습니다.


4
, 나뿐만 아니라이 문제를 본 적이 좋은
알렉스 Baranosky

28

SQL 안전

데이터를 수정할 SQL을 작성하기 전에 롤백 트랜잭션으로 전체를 래핑합니다.

BEGIN TRANSACTION
-- LOTS OF SCARY SQL HERE LIKE
-- DELETE FROM ORDER INNER JOIN SUBSCRIBER ON ORDER.SUBSCRIBER_ID = SUBSCRIBER.ID
ROLLBACK TRANSACTION

이렇게하면 잘못된 삭제 / 업데이트를 영구적으로 실행할 수 없습니다. 또한 모든 것을 실행하고 합리적인 레코드 수를 확인하거나 SELECTSQL과 사이 에 문을 추가 하여 ROLLBACK TRANSACTION모든 것이 올바르게 표시되도록 할 수 있습니다.

완전히있을 때 확실히 당신이 기대했던의 변경 않습니다 ROLLBACKCOMMIT와 진짜 실행합니다.


당신은 이것에 대한 펀치로 나를 이겼습니다! :-)
Howard Pinsley

2
나는 이것을 항상 사용했지만 DB 클러스터에 너무 많은 추가 오버 헤드가 발생하여 중지해야했습니다.
Zan Lynx

25

모든 언어의 경우 :

변수의 범위를 가능한 최소로 줄이십시오 . 다음 명령문으로 전달하기 위해 제공된 변수 를 피하십시오. 존재하지 않는 변수는 이해할 필요가없는 변수이며 책임을 질 수 없습니다. 같은 이유로 가능할 때마다 Lambdas를 사용하십시오.


5
eschew 부분이 정확히 무엇을 의미합니까? 때로는 다음 줄까지만 존재하는 변수를 도입합니다. 그것들은 표현식의 이름으로 사용되어 코드를 더 읽기 쉽게 만듭니다.
zoul

4
예, 동의하지 않습니다. 매우 복잡한 표현식의 경우 임시 변수를 사용하여 두 개 또는 더 짧고 간단한 표현식으로 나누는 것이 좋습니다. 유지 관리에 오류가 덜 발생하며 컴파일러는 온도를 최적화합니다
Cruachan

1
나는 명확성을 위해 변수를 선언하고 때로는 디버깅 대상을 만드는 예외적 인 경우가 있음에 동의합니다. 내 경험은 일반적인 관행은 반대 방향으로 잘못하는 것입니다.
dkretz 2016 년

나는 두 가지 견해에 동의한다. 그러나 표현식을 임시 변수로 나누면 별도의 범위 내에서 표현식을 시도합니다. 람다는 도우미 메소드와 마찬가지로 이것을 위해 좋습니다.
Erik Forbes

19

의심스러운 경우 응용 프로그램을 폭파하십시오!

각각의 모든 방법 의 시작 부분에서 각각모든 매개 변수를 확인 하고 (명확하게 코딩하거나 계약 기반 프로그래밍을 사용하는 것은 중요하지 않음) 올바른 사전 예외 및 / 또는 코드의 전제 조건이 충족시키지 못함.

우리 는 코드를 작성할 때 이러한 암시 적 전제 조건에 대해 알고 있지만, 명시 적으로 검사하지 않으면 나중에 문제가 발생했을 때 스스로 미로를 만들고 수십 번의 메소드 호출이 증상의 발생과 실제 위치를 분리합니다. 전제 조건이 충족되지 않은 경우 (= 문제 / 버그가 실제로있는 위치)


그리고 물론 : 일반 코드 (작은 라이브러리, C #의 확장 메소드 등)를 사용하면이를 쉽게 할 수 있습니다. 당신은 glike을 뭔가 쓸 수 있도록 param.NotNull("param")대신if ( param == null ) throw new ArgumentNullException("param");
peSHIr

2
또는 Spec #과 같은 계약 기반 프로그래밍을 사용하십시오!
bzlm

응용 프로그램에 따라 다릅니다. 플라이 바이 와이어 항공기 및 페이스 메이커 용 코드를 작성하는 사람들이 peSHIr처럼 생각하지 않기를 바랍니다.
MarkJ

6
@MarkJ : 당신은 정말로 그것을 얻지 않습니까? 만약 폭탄이 일찍 터지면 (= 개발자와 테스트 중), 생산 중에 폭탄을 터뜨리면 안됩니다. 그래서 난 정말 그들이 희망 이 같은 프로그램을!
peSHIr

특히 개인 및 보호 방법에 대해서는 그다지 마음에 들지 않습니다. 이유 : a) 비즈니스 요구 사항과 전혀 다른 검사로 코드를 복잡하게 만듭니다. b) 이러한 검사는 비공개 방법을 테스트하기가 어렵습니다. c) 많은 경우에 쓸모가 없습니다. 어쨌든 두 줄 이상
에리히 Kitzmueller

18

Java에서, 특히 콜렉션이있는 경우 API를 사용하십시오. 따라서 메소드가 List 유형 (예 : 유형)을 리턴하는 경우 다음을 시도하십시오.

public List<T> getList() {
    return Collections.unmodifiableList(list);
}

수업에서 피할 필요가없는 물건을 피하십시오!


+1 C #에는 이것에 대한 읽기 전용 컬렉션이 있습니다.
tobsen

1
Java의 경우 +1 나는 이것을 항상 사용한다
Fortyrunner

+1 ... 나는 이것을 많이한다 (더 많은 사람들이 이것을 기억하는 것을 원하지만).
Ryan Delucchi

기본 목록 변수의 인스턴스가 없는지 확인하십시오. 그렇지 않으면 그리 좋지 않을 것입니다 ...
GreenieMeanie

5
참고로, Collections.unmodifiableList는 불변의 반환 보기 목록이 아닌 불변의 사본을 . 따라서 원래 목록이 수정되면 뷰도 변경됩니다!
Zarkonnen

17

펄에서는

use warnings;

나는 좋아한다

use warnings FATAL => 'all';

이로 인해 컴파일러 / 런타임 경고로 인해 코드가 종료됩니다. 이것은 초기화되지 않은 문자열을 잡는 데 주로 유용합니다.

use warnings FATAL => 'all';
...
my $string = getStringVal(); # something bad happens;  returns 'undef'
print $string . "\n";        # code dies here

이것이 좀 더 광고 되었으면 좋겠다 ...
DJG

16

씨#:

string myString = null;

if (myString.Equals("someValue")) // NullReferenceException...
{

}

if ("someValue".Equals(myString)) // Just false...
{

}

Java와 아마도 대부분의 OO 언어에서 동일합니다.
MiniQuark

Objective-C에서는 메시지를 nil (특정 라이센스로 "널 객체의 호출 방법")에 보낼 수있는 것이 좋습니다. [nil isEqualToString : @ "Moo"]를 호출하면 false가 반환됩니다.
zoul

C # 예제에 동의하지 않습니다. 더 좋은 해결책은 "if (myString =="someValue ")"를 사용하는 것입니다. 또한 null 참조 예외가 없으며 더 읽기 쉽습니다.
Dan C.

13
이 예제는 프로그램에서 잠재적으로 위험한 상황을 숨길 수 있도록합니다. 널이 아닌 것으로 예상되지 않은 경우 예외를 처리하고 싶거나 널이 될 것으로 예상되는 경우이를 처리해야합니다. 이것은 나쁜 습관입니다.
rmeador

2
C #에서는 항상 string.Equals (<string1>, <string2>)를 사용합니다. 둘 중 하나가 null인지는 중요하지 않습니다.
Darcy Casselman

15

C #에서 string.IsNullOrEmpty를 확인하기 전에 length, indexOf, mid 등과 같은 문자열에 대한 작업을 수행하기 전에

public void SomeMethod(string myString)
{
   if(!string.IsNullOrEmpty(myString)) // same as myString != null && myString != string.Empty
   {                                   // Also implies that myString.Length == 0
     //Do something with string
   }
}

[편집]
이제 .NET 4.0에서 다음을 수행 할 수 있습니다. 값이 공백인지 추가로 확인합니다.

string.IsNullOrWhiteSpace(myString)

7
그것은 방어 적이 지 않고 문제를 무시하고 있습니다. 이어야 if (!string.IsNullOrEmpty(myString)) throw new ArgumentException("<something>", "myString"); /* do something with string */합니다.
enashnash

14

Java 및 C #에서 모든 스레드에 의미있는 이름을 지정하십시오. 여기에는 스레드 풀 스레드가 포함됩니다. 스택 덤프가 훨씬 더 의미가 있습니다. 스레드 풀 스레드에도 의미있는 이름을 지정하는 데 약간의 노력이 필요하지만 하나의 스레드 풀에 오래 실행되는 응용 프로그램에서 문제가 발생하면 스택 덤프가 발생할 수 있습니다 ( SendSignal.exe 에 대해 알고 있습니까? ), 로그를 잡고 실행중인 시스템을 중단하지 않고 어떤 스레드가 ... 무엇인지 알 수 있습니다. 교착 상태, 누출, 성장 등 문제가 무엇이든간에.


그리고-Windows에서-C ++ 용! (특별한 SEH 예외 처리 및 후속 캐치 사용).
Brian Haak

12

VB.NET을 사용하면 Visual Studio 전체에 대해 Option Explicit 및 Option Strict가 기본적으로 켜져 있습니다.


이전 코드로 작업하고 있지만 너무 엄격한 옵션이 설정되어 있지 않습니다 (너무 많은 컴파일러 오류가 발생하므로).이 두 옵션을 켜면 많은 마음을 구할 수 있습니다. 아픔.
Kibbee 2012 년

1
그건 그렇고, Option Strict를 사용하지 않는 오래된 코드를 사용하여 변환하는 사람은 String으로 자동 변환 된 항목의 경우 ToString ()을 사용하지 않습니다. 그들은 문자열로 캐스트를 사용하고 있습니다. 초기 .NET 시절에는 ToString ()을 사용하도록 변경했으며 특히 열거 형으로 인해 문제가 발생했습니다.
Ryan Lundy

10

C ++

#define SAFE_DELETE(pPtr)   { delete pPtr; pPtr = NULL; }
#define SAFE_DELETE_ARRAY(pPtr) { delete [] pPtr; pPtr = NULL }

그런 다음 모든 ' delete pPtr '및 ' delete [] pPtr '호출을 SAFE_DELETE (pPtr)SAFE_DELETE_ARRAY (pPtr)로 바꿉니다.

실수로 포인터를 삭제 한 후 포인터 'pPtr'을 사용하면 '액세스 위반'오류가 발생합니다. 무작위 메모리 손상보다 수정하는 것이 훨씬 쉽습니다.


1
템플릿을 사용하는 것이 좋습니다. 범위가 넓고 과부하가 가능합니다.

나는 단지 그렇게 말하려고했다. 매크로 대신 템플릿을 사용하십시오. 그렇게하면 코드를 단계별로 실행해야 할 수 있습니다.
Steve Rowe

나는 학교에서 어려운 길을 디버그하기가 더 쉬운 것을 배웠다. 그 이후로 내가 저지른 실수입니다. :)
Greg D

3
또는 Smart Pointers를 사용하십시오. 또는 도대체, 가능한 한 많이 새로운 / 삭제를 피하십시오.
Arafangion 2016 년

1
@Arafangion : 그냥 피하십시오 delete. 사용하여 new새 개체가 스마트 포인터에 의해 소유됩니다만큼, 괜찮습니다.
Alexandre C.

10

Java를 사용하면 어설 션이 해제 된 상태에서 프로덕션 코드를 실행하더라도 어설 션 키워드를 사용하는 것이 편리 할 수 ​​있습니다.

private Object someHelperFunction(Object param)
{
    assert param != null : "Param must be set by the client";

    return blahBlah(param);
}

어설 션이 꺼져 있어도 적어도 코드는 매개 변수가 어딘가에 설정 될 것이라는 사실을 문서화합니다. 이것은 비공개 도우미 함수이며 공개 API의 멤버가 아닙니다. 이 방법은 사용자 만 호출 할 수 있으므로 사용 방법에 대해 특정 가정을하는 것이 좋습니다. 공개 메소드의 경우 유효하지 않은 입력에 대해 실제 예외를 발생시키는 것이 좋습니다.


.NET에서 Debug.Assert는 동일한 작업을 수행합니다. "이 참조가 여기에 null 일 수 없습니다."라고 생각하는 곳이있을 때마다 Debug.Assert를 넣어서 null 있으면 버그를 수정하거나 가정을 변경할 수 있습니다.
Ryan Lundy

1
+1, 공개 방법이 계약 실패시, 개인이 주장해야 함
user7116

9

readonlyReSharper를 찾을 때까지 키워드를 찾지 못했지만 이제는 특히 서비스 클래스에서 본능적으로 키워드를 사용합니다.

readonly var prodSVC = new ProductService();

가능한 모든 필드에서 Java와 동등한 '최종'키워드를 사용합니다. 실제로 필드를 여러 번 설정할 수있는 필드를 설정하지 않거나 혼란스러운 스위치를 작성하는 것처럼 뼈대를 움직이지 않아도됩니다. 현지 변수 / 매개 변수를 표시 할 가능성은 적지 만 상처를 입을 수는 없습니다.
무법자 프로그래머

C #에서는 지역 변수를 읽기 전용으로 표시 할 수 없으므로 선택의 여지가 없습니다.
Jason Punyon

여기서 읽기 전용의 의미가 확실하지 않지만 Java의 최종 결과로는 충분하지 않습니다. 단지 객체에 대한 포인터가 변경되지 않았 음을 의미하지만 객체 자체의 변경을 막을 방법은 없습니다.
Slartibartfast

C #에서 읽기 전용 구조체는 변경되지 않도록합니다.
Samuel

9

Java에서 어떤 일이 일어나고 있는데 왜 그런지 모르겠다면 때때로 다음과 같이 Log4J를 사용할 것입니다.

if (some bad condition) {
    log.error("a bad thing happened", new Exception("Let's see how we got here"));
}

이렇게하면 예기치 않은 상황에 빠진 방법, 잠금 해제되지 않은 잠금 장치, null이 될 수없는 null 등의 방법을 보여주는 스택 추적이 표시됩니다. 분명히 실제 예외가 발생하면이 작업을 수행 할 필요가 없습니다. 이것은 실제로 다른 것을 방해하지 않고 프로덕션 코드에서 무슨 일이 일어나고 있는지 확인해야 할 때입니다. 나는 예외를 던지고 싶지 않아서 예외를 잡지 않았다. 적절한 메시지로 스택 추적을 기록하여 발생하는 상황을 표시하려고합니다.


흠, 이것은 다소 깔끔하지만 기능 및 오류 처리 코드가 섞여 있을까 걱정됩니다. 은 try-캐치 메커니즘이 서투른 편에 수 있지만, 모든 오류 처리이고, 이는 다른 (캐치)에 하나 개의 블록 (시도)에서 예외 라우트 실행을 만들기 위해 하나를 강제로
라이언 Delucchi

1
오류 처리에는 사용되지 않고 진단 에만 사용됩니다. 이를 통해 코드 흐름을 방해하지 않고 코드가 예기치 않은 상황에 도달 한 경로를 확인할 수 있습니다. 메소드 자체가 예기치 않은 상황을 처리 할 수 ​​있지만 여전히 발생해서는 안됩니다.
Eddie

2
new Exception ( "message"). printStackTrace ();를 사용할 수도 있습니다. 던지거나 잡을 필요는 없지만 여전히 로그에 좋은 스택 트레이스가 있습니다. 분명히 이것은 프로덕션 코드에 있어서는 안되지만 디버깅에 매우 유용 할 수 있습니다.
Jorn

9

Visual C ++를 사용하는 경우 기본 클래스의 메서드를 재정의 할 때마다 override 키워드를 사용하십시오 . 이런 식으로 누군가가 기본 클래스 서명을 변경하면 잘못된 메소드가 자동으로 호출되는 대신 컴파일러 오류가 발생합니다. 이전에 존재했다면 몇 번이나 절약했을 것입니다.

예:

class Foo
{
   virtual void DoSomething();
}

class Bar: public Foo
{
   void DoSomething() override { /* do something */ }
}

Java의 경우이 클래스는 클래스 Foo {void doSomething () {}}입니다. class Bar {@Override void doSomething () {}} 주석이 누락 된 경우 경고를 표시합니다. ) 및 주석이 있지만 아무것도 무시하지 않는 경우.
Jorn

니스 ... 난 너무 나쁜 마이크로 소프트의 특정 것 같다 ...이 일에 대해 알고하지 않았다 ...하지만 좋은의 # 정의는 휴대용하게하는 데 도움이 될 수 있습니다
e.tadeu

8

Java에서 무한히 오랜 시간이 걸릴 것으로 예상하지 않는 한 잠금을 해제하기를 무한정 기다리지 않는 법을 배웠습니다 . 현실적으로 잠금이 몇 초 안에 풀리면 일정 시간 동안 만 기다립니다. 잠금이 해제되지 않으면 스택에 불만을 제기하고 로그를 덤프하고 시스템 안정성에 가장 적합한 방법에 따라 잠금이 해제 된 것처럼 계속 진행하거나 잠금이 해제되지 않은 것처럼 계속 진행합니다.

이것은 내가 이것을 시작하기 전에 신비했던 몇 가지 경쟁 조건과 의사 교착 상태를 분리하는 데 도움이되었습니다.


1
이와 같이 사용 된 시간 초과로 대기하면 다른 더 나쁜 문제의 징조입니다.
dicroce 2018 년

2
가동 시간이 길어야하는 시스템에서는 문제를 찾을 수 있도록 최소한 진단 정보를 얻는 것이 좋습니다. 시스템이 알려진 상태에있을 때 스레드를 종료하거나 호출자에게 응답을 리턴하는 것을 선호하기 때문에 세마포어를 기다립니다. 그러나 당신은 영원히 매달려 싶지 않아
Eddie

8

씨#

  • 공개 메소드에서 참조 유형 매개 변수의 널이 아닌 값을 확인하십시오.
  • 나는 sealed원하지 않는 곳에 의존성을 도입하지 않기 위해 클래스에 많은 것을 사용 합니다. 상속을 허용하는 것은 우연이 아닌 명시 적으로 이루어져야합니다.

8

오류 메시지를 발행 할 때 최소한 오류 발생을 결정할 때 프로그램과 동일한 정보를 제공하십시오.

"권한이 거부되었습니다"는 권한 문제가 있음을 나타내지 만 문제의 원인 또는 위치를 모릅니다. "트랜잭션 로그 / my / file : 읽기 전용 파일 시스템을 쓸 수 없습니다"는 결정이 잘못 되었더라도, 특히 잘못된 경우, 특히 잘못된 경우 : 잘못된 파일 이름입니까? 잘못 열었습니까? 다른 예기치 않은 오류? -문제가 발생한 시점을 알려줍니다.


1
오류 메시지의 코드를 작성할 때 메시지를 읽고 다음에 알고 싶은 내용을 묻는 다음 추가하십시오. 불합리 할 때까지 반복하십시오. 예 : "범위를 벗어났습니다." 범위를 벗어난 것은 무엇입니까? "발이 범위를 벗어났습니다." 가치는 무엇입니까? "풋 카운트 (42)가 범위를 벗어났습니다." 범위는 무엇입니까? "푸 카운트 (42)가 범위를 벗어났습니다 (549-666)."
HABO

7

C #에서 as키워드를 사용하여 캐스트하십시오.

string a = (string)obj

obj가 문자열이 아닌 경우 예외가 발생합니다.

string a = obj as string

obj가 문자열이 아닌 경우 a를 null로 남겨 둡니다.

여전히 null을 고려해야하지만 일반적으로 캐스트 예외를 찾는 것이 더 간단합니다. 때때로 "캐스트 또는 블로우 업"유형 동작을 원할 경우(string)obj 원하는 구문이 선호됩니다.

내 코드에서는 as약 75 %의 (cast)구문과 약 25 %의 구문을 사용합니다.


맞지만 이제 참조를 사용하기 전에 null을 확인해야합니다.
Brian Rasmussen

7
얻지 못했습니다. null을 선호하는 나쁜 결정 인 것 같습니다. 원래 이유에 대한 힌트없이 런타임 동안 어딘가에 문제 발생합니다.
Kai Huppmann

예. 이것은 올바른 유형이 아닌 경우 실제로 널을 원할 경우에만 유용합니다. 경우에 따라 유용합니다. 컨트롤 로직과 디자인이 자주 분리되는 Silverlight에서는 컨트롤이 버튼 인 경우에만 "업"컨트롤을 사용하려고합니다. 존재하지 않으면 마치 존재하지 않는 것입니다 (= null).
샌더

2
이것은 요새의 큰 연회장 창문처럼 방어 적입니다. 그러나 그것은 멋진 전망입니다!
Pontus Gagge

1
이것은 '프로그램을 위반했기 때문에 예외를 사용하지 않은 사람을 상기시킵니다. 객체가 기대하는 클래스가 아닐 때 특정 동작을 원한다면 다른 방법으로 코딩 할 것이라고 생각합니다. is/instanceof마음에 봄.
Kirschstein

6

무엇이든 준비 하십시오 입력, 그리고 당신이 얻을 모든 입력은 로그가 예기치 않게, 덤프입니다. (이유 내에서 사용자로부터 암호를 읽는 경우 해당 로그를 덤프로 덤프하지 마십시오! 그리고 이러한 종류의 메시지를 초당 로그에 로그하지 마십시오. 로그하기 전에 내용 및 가능성 및 빈도에 대한 이유 .)

사용자 입력 유효성 검사에 대해서만 말하는 것이 아닙니다. 예를 들어, XML을 포함 할 것으로 예상되는 HTTP 요청을 읽는 경우 다른 데이터 형식을 준비하십시오. 내 요청이 내가 알지 못하고 고객이 무지하다고 주장한 투명한 프록시를 통과하는 것을 볼 때까지 XML 만 기대했던 HTML 응답을보고 놀랐고 프록시는 의뢰. 따라서 프록시는 클라이언트에게 XML 데이터 만 기대했던 혼란을 혼란스럽게하여 HTML 오류 페이지를 클라이언트에 반환했습니다.

따라서 와이어의 양쪽 끝을 제어한다고 생각하더라도 악의가없는 예기치 않은 데이터 형식을 얻을 수 있습니다. 예상치 못한 입력이 발생할 경우 방어 적으로 코드를 작성하고 진단 출력을 제공하십시오.


6

계약 방식으로 디자인을 사용하려고합니다. 모든 언어로 런타임을 에뮬레이트 할 수 있습니다. 모든 언어는 "어설 션"을 지원하지만 오류를보다 유용한 방식으로 관리 할 수있는 더 나은 구현을 작성하는 것은 쉽고 편리합니다.

에서 상위 25 가장 위험한 프로그래밍 오류 은 "부적절한 입력 유효성 검사"섹션 "구성 요소 사이에 안전하지 않은 상호 작용"에서 가장 위험한 실수입니다.

메소드 시작시 사전 조건 어설 션을 추가하면 매개 변수가 일관된 지 확인할 수 있습니다. 메소드의 끝에서 나는 사후 조건을 씁니다. 그 출력 할 inteded 무슨입니다 확인.

불변 을 구현하기 위해 , 나는 전제 조건과 사후 조건 매크로에 의해 정식으로 호출되어야하는 "클래스 일관성"을 검사하는 모든 클래스에 메소드를 작성합니다.

코드 계약 라이브러리를 평가하고 있습니다 .


6

자바

자바 API에는 불변 객체에 대한 개념이 없습니다. 이 경우 Final이 도움이 될 수 있습니다. 불변의 모든 클래스에 final을 태그하고 그에 따라 클래스를 준비하십시오. .

때로는 지역 변수에 final을 사용하여 값을 변경하지 않는 것이 유용한 경우가 있습니다. 나는 이것이 추악하지만 필요한 루프 구조에서 유용하다는 것을 알았습니다. 상수로 수정 ​​되었더라도 실수로 변수를 쉽게 재사용 할 수 있습니다.

게터에서 방어 복사 를 사용하십시오 . 기본 유형이나 변경 불가능한 객체를 반환하지 않으면 캡슐화를 위반하지 않도록 객체를 복사해야합니다.

clone을 사용하지 말고 copy 생성자를 사용하십시오 .

equals와 hashCode 사이의 계약을 배우십시오. 이것은 너무 자주 위반됩니다. 문제는 99 %의 경우 코드에 영향을 미치지 않는다는 것입니다. 사람들은 동등하게 덮어 쓰지만 hashCode는 신경 쓰지 않습니다. 코드에서 코드가 깨지거나 이상하게 작동하는 경우가 있습니다 (예 : 변경 가능한 객체를지도의 키로 사용).


5

echoPHP로 한 번 이상 쓰는 것을 잊었습니다 .

<td><?php $foo->bar->baz(); ?></td>
<!-- should have been -->
<td><?php echo $foo->bar->baz(); ?></td>

사실-> 그냥 에코하지 않을 때 왜-> baz ()가 아무것도 반환하지 않는지 알아 내려고 시도하는 데 영원히 걸릴 것입니다! : -S 그래서 나는 EchoMe반향되어야 할 값을 감쌀 수 있는 클래스를 만들었습니다 .

<?php
class EchoMe {
  private $str;
  private $printed = false;
  function __construct($value) {
    $this->str = strval($value);
  }
  function __toString() {
    $this->printed = true;
    return $this->str;
  }
  function __destruct() {
    if($this->printed !== true)
      throw new Exception("String '$this->str' was never printed");
  }
}

그리고 개발 환경을 위해 EchoMe를 사용하여 인쇄해야 할 것들을 포장했습니다.

function baz() {
  $value = [...calculations...]
  if(DEBUG)
    return EchoMe($value);
  return $value;
}

이 기술을 사용하여 첫 번째 예제에서 누락 된 echo예외가 발생합니다 ...


소멸자가 $ this-> printed! == true를 검사해서는 안됩니까?
Karsten

html에 php를 포함시키는 것은 템플릿 시스템을 사용하는 것이 거의 모든 시스템에서 차선책이라고 생각해야합니다.
Cruachan

어쩌면 내가 뭔가를 놓치고 있습니까? :하지만 코딩을 보상하기 위해 노력하고있다 이런 나에게 보이는 AnObject.ToString대신 Writeln(AnObject.ToString)?
환멸

네,하지만 실수는 PHP에서 만들어 훨씬 용이
너무 많은 PHP

4

C ++

new를 입력하면 즉시 delete를 입력해야합니다. 특히 배열의 경우.

씨#

특히 중개자 패턴을 사용할 때 특성에 액세스하기 전에 널을 점검하십시오. 객체는 전달 된 다음 (이미 언급 한대로 as를 사용하여 캐스트해야 함) null을 검사합니다. 널이 아니라고 생각하더라도 어쨌든 확인하십시오. 나는 놀랐다.


나는 당신의 첫 번째 포인트를 좋아합니다. 예를 들어 무언가 컬렉션을 반환하는 메서드를 작성할 때 비슷한 작업을 수행합니다. 첫 줄에 컬렉션을 만들고 즉시 return 문을 작성합니다. 남은 것은 컬렉션이 채워지는 방법을 채우는 것입니다.
무법자 프로그래머

5
C ++에서 new를 입력하면 해당 포인터를 AutoPtr 또는 참조 횟수 컨테이너에 즉시 할당해야합니다. C ++에는 소멸자와 템플릿이 있습니다. 삭제를 자동으로 처리하려면 현명하게 사용하십시오.
An̲̳̳

4

동적 런타임 로그 레벨 조정을 허용하는 로깅 시스템을 사용하십시오. 로깅을 활성화하기 위해 프로그램을 중지해야하는 경우 버그가 발생한 드문 상태를 잃게됩니다. 프로세스를 중지하지 않고 더 많은 로깅 정보를 켤 수 있어야합니다.

또한 리눅스의 'strace -p [pid]'는 시스템 호출이 프로세스 (또는 리눅스 스레드)가 만들고 싶어한다는 것을 보여줍니다. 처음에는 이상하게 보일 수 있지만 libc 호출에 의해 일반적으로 어떤 시스템 호출이 사용되는지 익숙해지면 현장 진단에서이 기능이 매우 유용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.