코드 계약 / 어설 션 : 중복 검사 란 무엇입니까?


10

나는 사용하는 언어로 주장, 계약 또는 수표를 작성하는 것을 좋아하는 팬입니다. 나를 조금 귀찮게하는 한 가지는 중복 검사를 처리하기위한 일반적인 관행이 무엇인지 확실하지 않다는 것입니다.

상황 예 : 먼저 다음 함수를 작성합니다

void DoSomething( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  //code using obj
}

몇 시간 후에 첫 번째 함수를 호출하는 다른 함수를 작성합니다. 모든 것이 메모리에서 여전히 DoSomething최신 상태 이므로 계약을 복제하지 않기로 결정합니다. 왜냐하면 null 객체를 이미 확인 해야한다는 것을 알기 때문입니다.

void DoSomethingElse( object obj )
{
  //no Requires here: DoSomething will do that already
  DoSomething( obj );
  //code using obj
}

명백한 문제 : DoSomethingElse이제 DoSomethingobj가 null이 아닌지 확인하는 데 달려 있습니다. 따라서 DoSomething더 이상 확인하지 않기로 결정하거나 다른 함수를 사용하기로 결정하면 obj가 더 이상 확인되지 않을 수 있습니다. 결국이 구현을 작성하게됩니다.

void DoSomethingElse( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  DoSomething( obj );
  //code using obj
}

상황이 커지면 동일한 물체를 여러 번 검사 할 수 있고 복제의 형태이며 우리 모두가 그렇게 좋지 않다는 것을 제외하고는 항상 안전하고 걱정할 필요가 없습니다.

이와 같은 상황에 대한 가장 일반적인 관행은 무엇입니까?


3
ArgumentBullException? 그것은 새로운 것입니다 :)
CVn

lol @ 내 타이핑 기술 ... 편집하겠습니다.
stijn

답변:


13

개인적으로 null을 얻지 못하면 실패하는 함수에서 null을 확인하고 그렇지 않은 함수는 null을 검사합니다.

따라서 위의 예제에서 doSomethingElse ()가 obj를 역 참조 할 필요가 없으면 obj를 null로 확인하지 않습니다.

DoSomething ()이 obj를 역 참조하면 null을 확인해야합니다.

두 함수가 모두 역 참조하면 둘 다 확인해야합니다. 따라서 DoSomethingElse가 obj를 역 참조하면 null을 확인해야하지만 DoSomething은 다른 경로에서 호출 될 수 있으므로 여전히 null을 확인해야합니다.

이렇게하면 코드를 상당히 깨끗하게 유지하고 검사가 올바른 위치에 있는지 확인할 수 있습니다.


1
난 전적으로 동의합니다. 각 방법의 전제 조건은 자체적으로 있어야합니다. DoSomething()전제 조건이 더 이상 필요하지 않도록 (이 특정 경우는 아니지만 다른 상황에서 발생할 수 있음) 다시 작성 하고 전제 조건 점검을 제거 한다고 가정 하십시오. 이제 전제 조건이 없어서 완전히 관련이없는 일부 방법이 깨졌습니다. 나는 몇 줄의 코드를 저장하려는 욕구에서 이상한 실패에 대해 명확하게하기 위해 약간의 코드 복제를 취할 것입니다.
CVn

2

큰! Code Contracts for .NET 에 대해 알게되었습니다 . 코드 계약은 평균 어설 션보다 훨씬 뛰어나며 정적 검사기 가 가장 좋은 예입니다. Visual Studio Premium 이상이 설치되어 있지 않으면이 기능을 사용하지 못할 수도 있지만 코드 계약을 사용하려는 경우에는 그 의도를 이해하는 것이 중요합니다.

계약을 함수에 적용하면 문자 그대로 계약 입니다. 이 기능은 계약에 따라 작동하며 계약에 정의 된 대로만 사용됩니다.

주어진 예제에서 DoSomethingElse()함수는 DoSomething()null이 전달 될 수 있으므로에 지정된대로 계약에 따라 실행되지 않으며 정적 검사기는이 문제를 나타냅니다. 이 문제를 해결하는 방법은에 동일한 계약을 추가하는 것 DoSomethingElse()입니다.

이제 이것은 중복이 있음을 의미하지만 두 기능에 걸쳐 기능노출 시키려면 이 중복이 필요 합니다. 이러한 함수는 비공개이지만 클래스의 다른 위치에서도 호출 할 수 있으므로 주어진 호출에서 인수가 null이되지 않도록하는 유일한 방법은 계약을 복제하는 것입니다.

이것은 왜 왜 행동을 두 가지 기능으로 나누 었는지 재고해야합니다. 한 장소에서만 호출되는 기능을 분할해서는 안된다는 것이 나의 의견 ( 일반적인 신념과는 달리 )이었다 . 계약을 적용함으로써 캡슐화를 공개함으로써 이것은 더욱 분명해진다. 내 대의에 대한 추가 주장을 찾은 것 같습니다! 감사합니다! :)


마지막 단락과 관련하여 : 실제 코드에서 두 함수는 서로 다른 두 클래스의 멤버이므로 분할되었습니다. 그 외에도 다음과 같은 상황에 여러 번있었습니다. 긴 함수를 작성하고 나누지 않기로 결정하십시오. 나중에 일부 논리가 다른 곳에 복제되어 있으므로 어쨌든 분할하십시오. 또는 1 년 후 다시 읽은 후 읽을 수 없으므로 어쨌든 분할하십시오. 또는 디버깅 할 때 : split 함수 = F10 키에서 덜 두드리는 현상. 더 많은 이유가 있기 때문에 때로는 너무 극단적 일 수도 있지만 개인적으로 나누기를 선호합니다.
stijn

(1) "나중에 일부 논리가 다른 곳에 중복되어 있음을 알아 내십시오 . " 그렇기 때문에 단순히 함수를 나누는 것보다 항상 "API를 향해 개발"하는 것이 더 중요합니다. 현재 수업뿐만 아니라 재사용에 대해 끊임없이 생각하십시오. (2) "1 년 후 다시 읽고 읽을 수 없음" 함수에는 이름이 있기 때문에 이것이 더 낫습니까? 내 블로그의 주석자가 "코드 단락"이라고 설명한 것을 사용하면 가독성이 훨씬 높아집니다 . (3) "분할 기능 = F10 키에서 덜 두드리는 소리" ... 왜 그런지 모르겠습니다.
Steven Jeuris

(1) 동의 함 (2) 가독성은 개인적 취향이므로 실제로 논의 할 내용은 아닙니다. (3) 20 줄의 기능을 수행하려면 F10을 20 번 누르십시오. 스플릿 기능에 필요한 10 개의 라인이있는 기능을 사용하면 F10을 11 번만 누르면됩니다. 예, 첫 번째 경우에는 중단 점을 넣거나 '커서로 점프'를 선택할 수 있지만 두 번째 경우보다 여전히 더 많은 노력입니다.
stijn

@stijn : (2) 동의했다; p (3) 설명해 주셔서 감사합니다!
Steven Jeuris
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.