모범 사례-함수 호출시 랩핑 vs 가드 인 함수에서 조기 종료 추가


9

나는 이것이 유스 케이스에 따라 다를 수 있다는 것을 알고 있지만, 너무 자주 궁금해합니다. 일반적으로 선호되는 구문이 있습니까?

함수에있을 때 가장 좋은 방법은 무엇인지 묻지 않고 일찍 종료 해야하는지 또는 함수를 호출하지 않아야하는지 묻습니다.

의 경우 주변의 함수 호출


if (shouldThisRun) {
  runFunction();
}

if ( guard ) 기능이 있어야 합니다

runFunction() {
  if (!shouldThisRun) return;
}

후자의 옵션은 분명히이 함수가 여러 번 호출되면 코드 중복을 줄일 가능성이 있지만 때로는 함수에 단일 책임을 잃을 수 있으므로 여기에 추가하는 것이 잘못되었다고 생각 합니다.


예를 들어 보자

내가 단순히 상태를 업데이트하는 updateStatus () 함수가 있다면. 상태가 변경된 경우에만 상태를 업데이트하고 싶습니다. 내 코드에서 상태가 변경 될 가능성이있는 장소를 알고 있으며, 변경이 필요한 다른 장소도 알고 있습니다.

나는 그 자체가 확실하지 않지만이 기능을 가능한 한 순수하게 유지하기 위해이 내부 기능을 확인하는 것이 다소 더럽습니다. 전화하면 상태가 업데이트 될 것으로 기대합니다. 그러나 변경되지 않을 가능성이있는 곳을 몇 군데 확인하여 통화를 포장하는 것이 더 나은지 여부는 알 수 없습니다.



3
@gnat 아니오, 그 질문은 본질적으로 '초기 종료에서 선호되는 구문은 무엇입니까?'반면에 내 것은 '초기 종료해야합니까 아니면 함수를 호출하지 않아야합니다'
Matthew Mullin

4
개발자 자신도 자신 이 호출하는 모든 곳에서 함수의 사전 조건을 올바르게 확인할 수는 없습니다 . 이러한 이유로 함수가 필요한 경우 내부에서 필요한 조건을 내부적으로 검증하는 것이 좋습니다.
TZHX

"상태가 변경된 경우에만 상태를 업데이트하고 싶습니다"-동일한 상태가 변경된 경우 상태를 업데이트 (= 변경) 하시겠습니까? 꽤 원형 인 것 같습니다. 이것의 의미를 명확하게 설명해 주시면 이에 대한 답변에 의미있는 예를 추가 할 수 있습니까?
Doc Brown

@DocBrown 예를 들어 두 개의 서로 다른 객체 상태 속성을 동기화하고 싶다고 가정하겠습니다. 객체가 변경되면 syncStatuses ()를 호출하지만 상태 필드뿐만 아니라 많은 다른 필드 변경에 대해 트리거 될 수 있습니다.
Matthew Mullin

답변:


15

함수 호출을 둘러싼 if : 함수를 호출
해야하는지 여부를 결정하고 프로그램의 의사 결정 프로세스의 일부입니다.

함수의 가드 절 (초기 리턴) :
유효하지 않은 매개 변수로 호출되는 것을 방지합니다.

이러한 방식으로 사용되는 보호 절은 "순수"(용어) 기능을 유지합니다. 함수가 잘못된 입력 데이터로 중단되지 않도록하기 위해서만 존재합니다.

함수 호출 여부에 대한 논리는 단지 아무리 높은 수준이라도 추상화 수준이 높습니다. 함수 자체 외부 에 존재해야합니다 . DocBrown이 말했듯이 코드를 단순화하기 위해이 검사를 수행하는 중간 기능을 사용할 수 있습니다.

이것은 좋은 질문이며, 추상화 수준을 인식하게하는 일련의 질문에 속합니다. 각 함수는 단일 추상화 수준에서 작동해야하며 함수에 프로그램 논리와 함수 논리가 모두 잘못된 느낌을 갖는 것은 서로 다른 추상화 수준에 있기 때문 입니다. 기능 자체는 하위 수준입니다.

이들을 별도로 유지하면 코드를보다 쉽게 ​​작성하고 읽고 유지 관리 할 수 ​​있습니다.


멋진 답변입니다. 나는 이것에 대해 생각할 수있는 명확한 방법을 제공한다는 사실을 좋아합니다. if는 함수가 먼저 호출되어야하는지에 대한 '의사 결정 프로세스의 일부'이므로 외부에 있어야합니다. 그리고 본질적으로 함수 자체와는 아무런 관련이 없습니다. 의견 답변을 올바른 것으로 표시하는 것이 이상하게 느껴지지만 몇 시간 후에 다시 살펴 보도록하겠습니다.
Matthew Mullin

그것이 도움이된다면, 나는 이것을 "의견"답으로 간주하지 않습니다. 나는 그것이 "느낌이 없다"고 생각하지만, 다른 추상화 레벨이 분리되어 있지 않기 때문이다. 내가 당신의 질문에서 얻은 것은 그것이 옳지 않다는 것을 알 수 있지만 추상화 수준에 대해 생각하지 않기 때문에 정량화하기가 어렵 기 때문에 말로 표현하기가 어렵습니다.
Baldrickk

7

매개 변수를 확인하지 않는 함수와 이와 같은 다른 함수를 모두 사용할 수 있습니다 (호출이 완료되었는지에 대한 정보를 반환 할 수 있음).

bool tryRunFunction(...)
{
    bool shouldThisRun = /* some logic using data not available inside "runFunction"*/;
    if (shouldThisRun)
        runFunction();
    return shouldThisRun;
}

이렇게하면 재사용이 가능 tryRunFunction하고 내부 검사를하지 않는 원래 (아마도 순수한) 기능을 제공함으로써 중복 논리를 피할 수 있습니다 .

때로는 tryRunFunction통합 검사와 독점적으로 같은 기능이 필요 하므로 검사를에 통합 할 수 runFunction있습니다. 또는 프로그램의 어느 곳에서나 다시 검사를 다시 사용할 필요가 없습니다.이 경우 호출 기능을 유지할 수 있습니다.

그러나 함수에 적절한 이름을 지정하여 발생하는 상황을 호출자에게 투명하게 만드십시오. 따라서 호출자는 스스로 검사해야하거나 호출 된 함수가 이미 수행 한 경우 구현을 추측하거나 구현할 필요가 없습니다. 이와 같은 간단한 접두사 try로 충분할 수 있습니다.


1
"tryXXX ()"관용구는 항상 약간 어색해 보이고 여기에는 부적절하다는 것을 인정해야합니다. 오류가 예상되는 작업을 수행하려고하지 않습니다. 더러워지면 업데이트 중입니다.
user949300

@ user949300 : 좋은 이름이나 명명 체계를 고르는 것은 실제 사용 사례, 실제 함수 이름에 달려 runFunction있습니다. 와 같은 기능에는와 같은 updateStatus()다른 기능이 수반 될 수 있습니다 updateIfStatusHasChanged(). 그러나 이것은 100 % 사례 의존이며, 이것에 대한 "한 가지 크기에 맞는"솔루션이 없으므로 "시도"관용구가 항상 좋은 선택은 아니라고 동의합니다.
Doc Brown

"dryRun"이라는 이름이 없습니까? 부작용없이 정기적으로 실행됩니다. 부작용을 비활성화하는 방법은 또 다른 이야기입니다
Laiv

3

누가 실행할 것인지를 결정하는 사람에 대한 답은 GRASP 의 "정보 전문가"라는 사람입니다.

일단 결정하면 명확성을 위해 함수의 이름을 바꾸는 것이 좋습니다.

함수가 결정하면 다음과 같이됩니다.

 ensureUpdated()
 updateIfDirty()

또는 발신자가 결정해야하는 경우 :

 writeStatus()

2

@Baldrickk의 답변을 확장하고 싶습니다.

귀하의 질문에 대한 일반적인 답변이 없습니다. 호출 할 함수의 의미 (계약) 및 조건의 특성에 따라 다릅니다.

따라서 call 예제에서 논의 해 봅시다 updateStatus(). 상태에 영향을 미치는 무언가가 발생했기 때문에 일부 상태를 업데이트하는 것이 계약입니다. 실제 상태 변경이없는 경우에도 해당 메소드에 대한 호출이 허용되고 실제 변경이있는 경우 필요합니다.

따라서 updateStatus()도메인 영역 내에서 관련 변경이 없음을 알고 있으면 호출 사이트에서 호출을 건너 뛸 수 있습니다 . 그것이 적절한 if구조로 외침을 둘러싸 야하는 상황 입니다.

updateStatus()함수 내 에서이 함수가 (도메인 영역 내의 데이터에서) 수행 할 작업이 없음을 감지하여 조기에 반환해야하는 상황이있을 수 있습니다.

따라서 질문은 다음과 같습니다.

  • 외부 관점에서, 기능 계약을 고려하여 언제 함수 호출이 허용 / 필요합니까?
  • 함수 내부에서 실제 작업없이 일찍 돌아올 수있는 상황이 있습니까?
  • 함수 호출 여부 / 초기 반환 여부 조건은 함수의 내부 도메인 또는 외부에 속하는가?

updateStatus()함수를 사용하면 변경 사항을 알지 못하는 사이트 호출, 호출 건너 뛰기 및 동일한 방식으로 동일한 조건을 두 번 확인하더라도 "변경되지 않은"상황을 조기에 확인하는 구현을 모두 볼 수 있습니다. 내부와 외부.


2

좋은 설명이 많이 있습니다. 그러나 나는 이상한 방식으로 보이고 싶다 : 당신이 이런 식으로 사용한다고 가정하자.

if (shouldThisRun) {
   runFunction();
}

runFunction() {
   if (!shouldThisRun) return;
}

그리고 다음 runFunction과 같은 방법으로 다른 함수를 호출해야합니다 .

runFunction() {
   if (!shouldThisRun) return;
   someOtherfunction();
}

무엇을 하시겠습니까? 모든 유효성 검사를 위에서 아래로 복사합니까?

someOtherfunction() {
   if (!shouldThisRun) return;
}

나는 그렇게 생각하지 않습니다. 따라서 나는 보통 같은 접근법을 수행합니다 : 입력의 유효성을 검사하고 public방법의 조건을 확인합니다 . 공개 메소드는 자체 검증을 수행해야하며 발신자에게도 필요한 조건을 확인해야합니다. 그러나 사적인 방법은 그 자체로 사업을하도록하라 . 다른 함수는 runFunction유효성 검사를 수행하거나 조건을 확인하지 않고 호출 할 수 있습니다.

public someFunction() {
   if (shouldThisRun) {
      runFunction();
   }
}

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