"거짓"인 부울 함수 인수에 대한 올바른 주석?


19

일부 오픈 소스 프로젝트에서 다음 코딩 스타일을 수집했습니다.

void someFunction(bool forget);

void ourFunction() {
  someFunction(false /* forget */);
}    

나는 항상 false여기 에 무슨 의미 가 있는지 의심 합니다. "forget"을 의미합니까, "forget"은 해당 매개 변수 (위의 경우와 같이)를 나타내며 "false"는이를 무시하는 것입니까?

어떤 스타일이 가장 자주 사용되며 모호성을 피하는 가장 좋은 방법은 무엇입니까?


38
bools 대신 enums (2 개의 옵션이 있더라도)를 사용하십시오
Esailija

21
일부 언어는 명명 된 인수를 지원합니다. 이러한 언어로 다음을 사용할 수 있습니다someFunction(forget: true);
Brian

3
나는 Flag Arguments 에 대한 Martin Fowler의 주장을 제시 할 의무가 있다고 느낀다 . 일반적으로 피하십시오.
FGreg

3
명백한 의견을 제시하기 위해 의견을 제시 할 수 있습니다. 일부 변경 때문에 따라서, 항상 더 나은 코드 자체 문서화을하는 것입니다 true으로 false하고 주석을 업데이트하지. API를 변경할 수 없다면이를 언급하는 가장 좋은 방법은 다음과 같습니다.someFunction( false /* true=forget, false=remember */)
Mark Lakata

1
@Bakuriu-실제로 공개 API에는 두 가지 별도의 메소드 ( sortAscendingsortDescending유사한)가 있습니다. 이제, 내부 , 그들은 모두 매개 변수의 종류가있을 수 있습니다 동일한 개인 메서드를 호출 할 수 있습니다. 실제로, 언어가이를 지원한다면, 아마도 전달할 것은 정렬 방향을 포함하는 람다 함수일 것입니다.
Clockwork-Muse

답변:


33

게시 한 샘플 코드 forget에서 플래그 인수 인 것처럼 보입니다 . (이 함수는 순전히 가상이기 때문에 확신 할 수 없습니다.)

플래그 인수는 코드 냄새입니다. 함수는 둘 이상의 작업을 수행하며 올바른 기능은 한 가지 작업 만 수행해야 함을 나타냅니다.

플래그 인수를 피하려면 함수를 함수 이름의 차이점을 설명하는 두 개의 함수로 나눕니다.

플래그 인수

serveIceCream(bool lowFat)

깃발 인수 없음

serveTraditionalIceCream()
serveLowFatIceCream()

편집 : 이상적으로는 플래그 매개 변수를 사용하여 함수를 유지할 필요가 없습니다. 무엇의 라인을 따라 경우가 있습니다 파울러가 얽힌 구현 호출 완전히 기능을 분리하는 것은 중복 된 코드를 생성합니다. 그러나 매개 변수화 된 함수의 순환 복잡성이 높을수록이를 제거하는 주장이 더 강력 해집니다.


이것은 직감 일 뿐이지 만 매개 변수는 forget기능 부러워합니다. 발신자가 다른 개체에게 무언가를 잊어 버리라고 말하는 이유는 무엇입니까? 더 큰 디자인 문제가있을 수 있습니다.


4
+17, 헬멧 착용. 의 몸 무엇 serveTraditionalIceCreamserveLowFatIceCream같은 모습은? 나는 14 종류의 아이스크림으로 열거 형을 가지고 있습니다.
JohnMark13

13
퍼블릭 메서드의 경우이 규칙은 좋지만 JohnMark가 주장한 것처럼 SRP의 나머지 절반은 "좋은 방법은 그 기능을 수행하는 유일한 방법이어야합니다"입니다. 이 방법의 N + 1 변형이있는 것을 볼 수 있습니다. 매개 변수가없는 N 공개, 모두 노출을 피하려는 매개 변수를 사용하여 하나의 개인 메서드를 호출합니다. 어느 시점에서, 당신은 단지 지독한 매개 변수를 포기하고 노출시킵니다. 코드 냄새는하지 않습니다 반드시 평균 코드 리팩토링해야한다; 그것들은 코드 검토에서 두 번째 모습을 볼 가치가있는 것입니다.
KeithS

@Aaron "feature envy"란 무슨 뜻입니까?
Geek

27

평신도의 말로 :

  • false 리터럴입니다.
  • 당신은 문자 그대로 전달 false
  • 당신은 someFunction잊지 말라고
  • 당신은 말하고있다 someFunction매개 변수는 잊어false
  • 당신은 someFunction기억하고 있습니다

내 의견으로는 기능이 다음과 같으면 더 좋을 것입니다.

void someFunction(bool remember);

당신은 그것을 호출 할 수 있습니다

void ourFunction() {
  someFunction(true);
} 

또는 이전 이름을 유지하지만 래퍼 기능을

void ourFunctionWithRemember() {
  someFunction(false);
} 

편집하다:

@Vorac이 언급했듯이 항상 긍정적 인 단어를 사용하려고 노력하십시오. 이중 부정은 혼란 스럽다.


15
아이디어가 항상 긍정적 인 단어를 사용하도록 노력하기 위해 +1. 이중 부정은 혼란 스럽다.
Vorac

1
동의하고 좋은 생각입니다. 시스템에 무언가를하지 말라고하는 것은 혼란 스럽다. 부울 매개 변수를 승인하면 긍정적으로 표현하십시오.
Brandon

대부분의 경우 remember함수 이름이 remember 매우 분명한 의미를 갖지 않는 한, 당신은 또한보다 구체적이기를 원한다고 생각합니다 . rememberToCleanUp* or *persist또는 뭔가.
itsbruce

14

매개 변수의 이름은 잘 지정 될 수 있습니다. 함수의 이름을 모르면 말하기가 어렵습니다. 나는 의견이 기능의 원래 저자에 의해 쓰여진 가정, 그것은 지나가는 것을 상기시켜이었다 falsesomeFunction수단을하지만, 사람이 이후에 함께와 주셔서 먼저 눈에 약간 불분명하다.

예를 들어 Code Complete 에서 제안 된 양의 변수 이름을 사용하면 이 스 니펫을보다 쉽게 ​​읽을 수있는 가장 간단한 변경 사항이 될 수 있습니다.

void someFunction(boolean remember);

다음 ourFunction이된다 :

void ourFunction() {
    someFunction(true /* remember */);
}

그러나 열거 형을 사용하면 일부 지원 코드를 희생하면서 함수 호출을 이해하기가 더 쉽습니다.

public enum RememberFoo {
    REMEMBER,
    FORGET
}

...

void someFunction(RememberFoo remember);

...

void ourFunction() {
    someFunction(RememberFoo.REMEMBER);
}

someFunction어떤 이유로 든 서명을 변경할 수없는 경우 임시 변수를 사용하면 코드를 더 쉽게 읽을 수 있습니다. 인간이 코드를 쉽게 구문 분석 할 수있게하는 것 외에 다른 이유없이 변수를 도입하여 조건부를 단순화하는 것과 같습니다 .

void someFunction(boolean remember);

...

void ourFunction() {
    boolean remember = false;
    someFunction(remember);
}

1
remembertrue로 설정 하면 (예제에서 someFunction(true /* forget */);) 잊어 버리는 것을 의미 합니까?
Brian

2
enum지금까지 가장 좋은 방법입니다. 유형을해서 할 수 A와 표현 bool- 즉, 그들은 동형 - 그것은이 것을 의미하지 않는다 한다 등으로 표현 될 수있다. 같은 주장이 string및 짝수에도 적용됩니다 int.
Jon Purdy

10

부울 값이 의미가 있도록 변수 이름을 바꿉니다.

이름이 모호하기 때문에 함수에 인수를 설명하기 위해 주석을 추가하는 것보다 백만 배 더 좋습니다.


3
그것은 질문에 대답하지 않습니다. 메소드가 정의되고 4 줄 떨어진 동일한 파일에서 호출되면 모든 것이 분명합니다. 그러나 현재보고있는 모든 사람이 발신자라면? 부울이 여러 개인 경우 어떻게합니까? 때로는 간단한 인라인 주석이 먼 길을갑니다.
Brandon

@Brandon 그것은 부울 doNotPersist (또는 더 나은 Persist)를 호출하는 것에 대한 논쟁이 아닙니다. 잊을 것을 말하지 않고 "잊어"라고하는 것은 솔직히 도움이되지 않습니다. 아, 그리고 옵션으로 여러 부울을 취하는 방법은 높은 하늘에 악취가납니다.
itsbruce

5

보다 설명적인 이름으로 로컬 부울을 작성하고 값을 지정하십시오. 그렇게하면 의미가 더 명확 해집니다.

void ourFunction() {
    bool takeAction = false;  /* false means to forget */
    someFunction( takeAction );
}    

변수의 이름을 바꿀 수 없다면 주석은 좀 더 표현력이 있어야합니다.

void ourFunction() {
    /* false means that the method should forget what was requested */
    someFunction( false );
}    

1
확실히 좋은 조언이지만, /* forget */주석이 해결해야 할 문제를 해결하지 못한다고 생각합니다. 즉, 함수 앞에 선언하지 않으면 설정된 내용을 기억하기가 어려울 수 있습니다 false. (왜 열거 형을 추가하라는 @Esailija의 조언이 더 좋으며, 명명 된 매개 변수를 허용하는 언어를 좋아하는 이유는 무엇입니까?)
Gort the Robot

@StevenBurnap-감사합니다! 당신은 내 옛 대답이 OP의 질문을 다루기에 충분하지 않다는 것에 맞습니다. 더 명확하게 편집했습니다.

3

Qt-Style API를 참조 할 때이 정확한 상황을 언급하는 좋은 기사가 있습니다. Boolean Parameter Trap 이라고 불리우며 읽을만한 가치가 있습니다.

그것의 요지는 :

  1. bool이 필요하지 않도록 함수를 오버로드하는 것이 좋습니다
  2. Esailija가 제안한 것처럼 열거 형을 사용하는 것이 가장 좋습니다.

2

이것은 기괴한 의견입니다.

컴파일러의 관점에서 볼 때 someFunction(false /* forget */);실제로 someFunction(false);주석이 제거됩니다. 따라서 모든 행은 someFunction첫 번째 (및 유일한) 인수를로 설정하여 호출합니다 false.

/* forget */매개 변수의 이름 일뿐입니다. 아마도 (더럽고 더러운) 알림 일뿐입니다. 실제로 거기에있을 필요는 없습니다. 덜 모호한 매개 변수 이름을 사용하면 주석이 전혀 필요하지 않습니다.


1

Clean 코드 의 조언 중 하나는 불필요한 주석의 수를 1 줄이고 (썩는 경향이 있기 때문에) 함수와 메소드의 이름을 올바르게 지정하는 것입니다.

그런 다음 주석을 제거합니다. 결국, Eclipse와 같은 최신 IDE는 마우스를 함수 위에 놓으면 코드가있는 상자를 표시합니다. 코드를 보면 모호성이 없어집니다.


1 복잡한 코드를 주석 처리해도됩니다.


btw 누가 이런 말을했는지 : "가장 최악의 프로그래머의 문제는 변수의 이름을 정하는 방법과 1 씩 오프셋하는 방법"입니까?
BЈовић

4
당신은 아마 그것의 출처로 martinfowler.com/bliki/TwoHardThings.html 을 찾고있을 것 입니다. "컴퓨터 과학에는 캐시 무효화, 이름 지정 및 한 번의 오류로 인해 두 가지 어려운 점만 있습니다"라는 내용을 들었습니다.

1

명백한 의견을 제시하기 위해 의견을 제시 할 수 있습니다. 어떤 사람이 (아마도 당신이) 변경 때문에 따라서, 설명하는 주석에 의존하지 않고 코드 자체 문서화를 만들기 위해 항상 더 나은 truefalse와 주석을 업데이트하지.

API를 변경할 수 없으면 2 가지 옵션을 사용합니다

  • 코드에 관계없이 항상 주석이되도록 주석을 변경하십시오. 한 번만 호출하면 문서를 로컬로 유지하므로 좋은 해결책입니다.
     someFunction (거짓 / * true = 잊어 버림, false = 기억 * /);`
  • 특히 두 번 이상 호출 할 경우 #defines를 사용하십시오.
     # 정의를 잊어 버리십시오
     #define REMEMBER false
     someFunction (리멤버);

1

나는 주석을 항상 참으로 만드는 것에 대한 대답 을 좋아하지만, 좋지만이 코드의 근본적인 문제를 놓친다고 생각합니다. 문자 그대로 호출됩니다.

메소드를 호출 할 때 리터럴을 사용하지 않아야합니다. 지역 변수, 선택적 매개 변수, 명명 된 매개 변수, 열거 형-가장 잘 피하는 방법은 언어와 사용 가능한 언어에 따라 다르지만 피하려고합니다. 리터럴에는 값이 있지만 의미가 없습니다.


-1

C #에서는 명명 된 매개 변수를 사용하여이를 명확하게했습니다.

someFunction(forget: false);

또는 enum:

enum Memory { Remember, Forget };

someFunction(Memory.Forget);

또는 과부하 :

someFunctionForget();

또는 다형성

var foo = new Elephantine();

foo.someFunction();

-2

이름은 항상 부울에 대한 모호성을 해결해야합니다. 항상 'isThis'또는 'shouldDoThat'과 같은 부울 이름을 지정합니다. 예를 들면 다음과 같습니다.

void printTree(Tree tree, bool shouldPrintPretty){ ... }

등등. 그러나 다른 사람의 코드를 참조 할 때 값을 전달할 때 주석을 남기는 것이 가장 좋습니다.


이것은 질문에 어떻게 대답합니까?
gnat

@ gnat 부울 매개 변수로 모호성을 해결하는 것에 대한 그의 질문에 대답했습니다. 어쩌면 나는 그의 질문을 잘못 읽었을 것이다.
dchhetri
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.