동작을 결정하기 위해 부울 매개 변수를 사용하는 것이 잘못 되었습니까?


194

나는 때때로“느끼는”느낌이 드는 관행을 보았지만, 그것에 대해 무엇이 잘못되었는지 분명히 말할 수는 없다. 아니면 내 편견 일 수도 있습니다. 간다 :

개발자는 부울을 매개 변수 중 하나로 사용하여 메서드를 정의하고 해당 메서드는 다른 메서드 등을 호출하며 결국 부울이 사용되어 특정 작업을 수행할지 여부를 결정합니다. 예를 들어 사용자에게 특정 권한이 있거나 테스트 모드 또는 배치 모드 또는 라이브 모드에 있거나 시스템이 아닌 경우에만 작업을 허용하기 위해 사용할 수 있습니다. 특정 상태.

매개 변수를 전달하지 않고 작업을 수행 할 시간을 쿼리하거나 여러 버전의 메서드를 사용하거나 클래스를 여러 번 구현하는 등의 방법으로 항상 다른 방법이 있습니다. 내 질문은 이 방법을 개선하는 방법은 많지 않지만 실제로 (실제로 의심되는) 잘못된 것인지 아닌지, 무엇이 잘못 되었습니까?


1
이것은 결정이 어디에 속하는 지에 대한 문제입니다. 모든 결정을 흩 뜨리지 말고 중앙 위치로 결정을 옮기십시오. 이렇게하면 if가있을 때마다 두 번째 코드 경로를 갖는 것보다 복잡성이 줄어 듭니다.

28
Martin Fowler는 이것에 관한 기사를 가지고 있습니다 : martinfowler.com/bliki/FlagArgument.html
Christoffer Hammarström


@ ChristofferHammarström Nice 링크. 나는 내 설명과 같은 아이디어를 훨씬 자세하게 설명하면서 대답에 포함시킬 것입니다.
Alex

1
나는 Nick이 말한 것에 항상 동의하지는 않지만이 경우 100 %에 동의합니다 . 부울 매개 변수를 사용하지 마십시오 .
Marjan Venema

답변:


107

예, 이것은 코드 냄새 일 수 있습니다. 이로 인해 이해하기 어렵고 쉽게 재사용 할 가능성이 낮은 유지 관리 할 수없는 코드가 생길 수 있습니다.

다른 포스터가 말했듯이 컨텍스트는 모든 것입니다 (하나가 꺼져 있거나 나중에 다시 리팩터링하기 위해 의도적으로 발생하는 기술 부채로 인정 된 경우 손으로 쓰지 마십시오). 그러나 매개 변수가 전달되면 광범위하게 말하기 실행될 특정 행동을 선택하는 기능으로 들어가면 단계적인 개선이 필요하다. 이 기능을 더 작은 기능으로 나누면보다 응집력이 높은 기능이 생성됩니다.

그렇다면 응집력이 높은 기능은 무엇입니까?

한 가지 일만하는 기능입니다.

설명 할 때 전달 된 매개 변수의 문제점은 함수가 두 가지 이상의 작업을 수행한다는 것입니다. 부울 매개 변수의 상태에 따라 사용자 액세스 권한을 검사하거나 검사하지 않을 수 있습니다. 그런 다음 의사 결정 트리에 따라 기능을 수행합니다.

액세스 제어 문제를 태스크, 조치 또는 명령 문제와 분리하는 것이 좋습니다.

이미 언급했듯이 이러한 우려가 서로 얽혀있는 것처럼 보입니다.

따라서 응집성 (Cohesiveness)이라는 개념은 문제의 기능이 응집력이 높지 않고 코드를 리팩토링하여 일련의 응집력있는 기능을 생성 할 수 있음을 식별하는 데 도움이됩니다.

그래서 질문은 다시 풀릴 수있었습니다. 행동 선택 매개 변수를 전달하는 것이 문제를 개선하는 방법을 피하는 것이 가장 좋습니다.

매개 변수를 완전히 제거합니다. 테스트를 위해 액세스 제어를 해제하는 기능은 잠재적 인 보안 위험입니다. 테스트 목적으로 액세스 허용 또는 액세스 거부 시나리오를 테스트하기 위해 액세스 점검을 또는 합니다.

참고 : 응집력 (컴퓨터 과학)


롭, 응집력이 무엇이고 어떻게 적용되는지에 대해 설명해 주시겠습니까?
Ray

Ray, 이것에 대해 더 많이 생각할수록 액세스 제어를 켜는 부울이 응용 프로그램에 도입되는 보안 구멍을 기반으로 코드에 반대해야한다고 생각합니다. 코드베이스의 품질 향상은 좋은 부작용이 될 것입니다.)
Rob

1
응집력에 대한 아주 좋은 설명과 적용입니다. 이것은 정말로 더 많은 표를 얻어야합니다. 그리고 보안 문제에도 동의합니다. 비록 그들이 모두 개인적인 방법이라면 잠재적으로 더 작은 취약점입니다
Ray

1
고마워요 레이. 시간이 허락 할 때 리팩토링하기에 충분히 쉬운 것처럼 들린다. 이 문제를 강조하기 위해 TODO 의견을 제시 할 가치가있을 수 있습니다. 기술 권한과 때로는 일을 끝내야하는 압력에 대한 민감도 사이의 균형을 맞 춥니 다.
Rob

"unmaintainable"
aaa90210

149

아주 간단한 이유로이 패턴을 오래 전에 사용을 중단했습니다. 유지 보수 비용. 여러 번 frobnicate(something, forwards_flag)코드에서 여러 번 호출 된 함수가 있다는 것을 알았고 값 false이 의 값 으로 전달 된 코드의 모든 위치를 찾아야했습니다 forwards_flag. 쉽게 검색 할 수 없으므로 유지 관리 문제가 발생합니다. 각 사이트에서 버그 수정을해야하는 경우, 하나를 놓치면 불행한 문제가있을 수 있습니다.

그러나이 특정 문제는 접근 방식을 근본적으로 변경하지 않고도 쉽게 해결할 수 있습니다.

enum FrobnicationDirection {
  FrobnicateForwards,
  FrobnicateBackwards;
};

void frobnicate(Object what, FrobnicationDirection direction);

이 코드를 사용하면의 인스턴스 만 검색하면됩니다 FrobnicateBackwards. 변수 에이 코드를 할당하는 코드가있을 수 있으므로 몇 가지 제어 스레드를 따라야하지만 실제로는이 대안이 제대로 작동하기에 충분히 드물다는 것을 알았습니다.

그러나 최소한 원칙적으로 이러한 방식으로 플래그를 전달하는 데에는 또 다른 문제점이 있습니다. 이것은이 설계를 가진 일부 (일부) 시스템이 코드의 깊게 중첩 된 부분 (플래그를 사용)의 구현 세부 사항에 대해 너무 많은 지식을 외부 계층에 전달할 수 있다는 것입니다. 이 플래그에서). Larry Constantine의 용어를 사용하기 위해이 디자인은 setter와 boolean 플래그 사용자간에 강한 결합을 가질 수 있습니다 . Franky는 코드베이스에 대해 더 많이 알지 못하고이 질문에 대해 어느 정도 확신을 가지고 말하기가 어렵습니다.

당신이 제공하는 구체적인 예를 다루기 위해, 나는 각각 어느 정도의 관심을 가질 것이지만 주로 위험 / 정확성의 이유로 인해 있습니다. 즉, 시스템이 시스템의 상태를 나타내는 플래그를 전달 해야하는 경우이를 고려해야하지만 매개 변수를 확인하지 않는 코드가 있음을 알 수 있습니다 (전달되지 않았기 때문에) 이 기능). 따라서 누군가 매개 변수 전달을 생략했기 때문에 버그가 있습니다.

또한 거의 모든 기능에 전달해야하는 시스템 상태 표시기가 사실상 전역 변수라는 것을 인정할 가치가 있습니다. 전역 변수의 많은 단점이 적용됩니다. 많은 경우에 시스템 상태 (또는 사용자의 자격 증명 또는 시스템의 ID)에 대한 정보를 해당 데이터를 기반으로 올바르게 작동하는 개체 내에 캡슐화하는 것이 더 좋습니다. 그런 다음 원시 데이터가 아닌 해당 객체에 대한 참조를 전달합니다. 여기서 핵심 개념은 캡슐화 입니다.


9
우리가 다루는 내용의 본질과 그것이 우리에게 어떤 영향을 미치는지에 대한 통찰력뿐만 아니라 정말 훌륭한 구체적인 예.
Ray

33
+1. 나는 이것을 위해 최대한 열거 형을 사용합니다. bool나중에 추가 매개 변수가 추가 된 함수를 보았고 호출은 다음과 같습니다 DoSomething( myObject, false, false, true, false ). 추가 부울 인수의 의미를 파악하는 것은 불가능하지만 의미있는 이름의 열거 형 값을 사용하면 쉽습니다.
Graeme Perrow

17
오, 마침내 FrobnicateBackwards 방법에 대한 좋은 예입니다. 이것을 영원히 검색했습니다.
Alex Pritchard

38

반드시 잘못된 것은 아니지만 코드 냄새를 나타낼 수 있습니다 .

부울 매개 변수와 관련하여 피해야하는 기본 시나리오는 다음과 같습니다.

public void foo(boolean flag) {
    doThis();
    if (flag)
        doThat();
}

그럼 당신은 일반적으로 부르는 것 호출 할 때 foo(false)foo(true)원하는 정확한 동작에 따라 달라집니다.

응집력이 좋지 않기 때문에 이것은 실제로 문제가됩니다. 실제로 필요하지 않은 메소드 간 종속성을 작성 중입니다.

당신이이 경우에 일을해야하는 것은 떠나 doThisdoThat별도의 공공 방법으로 다음 일을 :

doThis();
doThat();

또는

doThis();

이렇게하면 커플 링을 만들지 않고 호출자에게 정확한 결정을 내릴 수 있습니다 (부울 매개 변수를 전달한 것처럼).

물론 모든 부울 매개 변수가 그렇게 나쁜 방식으로 사용되는 것은 아니지만 코드 냄새가 나는 것이므로 소스 코드에서 많이 볼 경우 의심 스러울 것입니다.

이것은 내가 쓴 예제를 기반 으로이 문제를 해결하는 방법의 한 예입니다. 다른 접근 방식이 필요한 다른 경우도 있습니다.

Martin Fowler 의 동일한 기사에 대해 자세히 설명 하는 좋은 기사가 있습니다 .

추신 : foo두 개의 간단한 메소드를 호출 하는 대신 메소드가 더 복잡한 구현을했다면 작은 리팩토링 추출 메소드를 적용하기 만하면 결과 코드가 foo내가 작성한 구현과 유사하게 보입니다 .


1
"코드 냄새"라는 용어를 불러 주셔서 감사합니다. 냄새가 나쁘다는 것을 알았지 만 냄새가 무엇인지 알 수 없었습니다. 귀하의 예는 내가보고있는 것과 거의 관련이 있습니다.
Ray

24
if (flag) doThat()내부 foo()가 합법적 인 상황이 많이 있습니다 . doThat()모든 호출자 호출 에 대한 결정을 푸시 하면 나중에 일부 메소드를 찾은 경우 제거해야하는 반복을 강제로 수행 flag해야합니다 doTheOther(). 나중에 모든 호출자를 문지르는 것보다 같은 클래스의 메소드간에 종속성을 설정하는 것이 좋습니다.
Blrfl

1
@Blrfl : 그렇습니다. 더 간단한 리팩토링은 a doOnedoBothmethod 를 생성하거나 (각각 false와 true 인 경우) James Youngman이 제안한 것처럼 별도의 열거 형을 사용하는 것이라고 생각합니다
hugomg

@missingno : 중복 코드를 호출자에게 보내 doOne()거나 doBoth()결정을 내리는 것과 같은 문제가 여전히 있습니다 . 서브 루틴 / 함수 / 방법에는 인수가 있으므로 동작이 달라질 수 있습니다. 실제로 부울 조건에 열거 형을 사용하면 인수 이름에 이미 설명되어있는 경우 반복하는 것처럼 들립니다.
Blrfl

3
두 개의 메소드를 차례로 호출하거나 하나의 메소드 만 호출하는 것이 중복으로 간주 될 수 있다면 try-catch 블록을 작성하는 방법이나 다른 경우에도 중복됩니다. 그것들을 모두 추상화하는 함수를 작성한다는 의미입니까? 아니! 하나의 메소드를 작성하는 것만으로도 두 개의 다른 메소드를 호출하는 것이 반드시 좋은 추상화를 나타내는 것은 아닙니다.
Alex

29

첫째, 프로그래밍은 예술만큼 과학이 아닙니다. 따라서 프로그래밍하는 "잘못된"방법과 "올바른"방법은 거의 없습니다. 대부분의 코딩 표준은 일부 프로그래머가 유용하다고 생각하는 "기본 설정"일뿐입니다. 그러나 궁극적으로 그들은 다소 임의적입니다. 따라서 매개 변수 선택에 그 자체로 "잘못된"레이블을 지정하지 않았으며 부울 매개 변수만큼 일반적이고 유용한 것은 아닙니다. 상태를 캡슐화하기 위해 a boolean(또는 int그 문제에 대한)를 사용하는 것은 많은 경우에 완벽하게 정당화됩니다.

코딩 결정은 대체로 성능 및 유지 관리 가능성을 기반으로해야합니다. 성능이 문제가되지 않으면 (그리고 귀하의 사례에서 그 성능이 어떨지 상상할 수없는 경우), 다음 고려 사항은 다음과 같습니다. 나 (또는 ​​미래의 편집자)가 유지하기가 얼마나 쉬운가? 직관적이고 이해할 수 있습니까? 격리되어 있습니까? 체인 함수 호출의 예는 실제로 이와 관련하여 잠재적으로 부서지기 쉬운 것 같습니다.를로 변경하기 bIsUp로 결정 bIsDown하면 코드의 다른 위치도 변경해야합니까? 또한, 당신의 패러미터리스트가 풍선처럼 보이나요? 함수에 17 개의 매개 변수가있는 경우 가독성이 문제이므로 객체 지향 아키텍처의 이점을 이해하고 있는지 다시 고려해야합니다.


4
첫 단락의 경고에 감사드립니다. 저는 "잘못"을 말함으로써 의도적으로 도발적인 것이 었으며, 우리가 "모범 사례"와 디자인 원칙의 영역을 다루고 있으며 이러한 종류의 상황이 종종 상황에 따라 여러 요인을 고려해야한다는 점을 분명히 인정합니다
Ray

13
당신의 대답은 내가 소스를 기억할 수 없다는 인용문을 상기시켜줍니다- "함수에 17 개의 매개 변수가 있으면 아마도 누락 된 매개 변수입니다".
Joris Timmermans

나는 이것에 매우 동의하고, 종종 부울 플래그를 전달하는 것은 좋지 않은 생각이라고 말하기 위해 질문에 적용하지만, 결코 나쁜 / 좋은 것만 큼 간단하지는 않습니다.
JohnB

15

Robert C Martins Clean 코드 기사에 따르면 부울 인수는 메서드가 두 가지 이상을 수행하므로 가능한 한 부울 인수를 제거해야한다고 말합니다. 방법은 한 가지 일만해야한다고 생각합니다. 그의 좌우명 중 하나라고 생각합니다.


@dreza 당신은 Curlys Law를 언급하고 있습니다.
MattDavey

물론 경험이 있다면 그러한 주장을 언제 무시해야하는지 알아야합니다.
gnasher729

8

여기서 가장 중요한 것은 실용적이라고 생각합니다.

부울이 전체 동작을 결정할 때 두 번째 방법을 만드십시오.

부울이 중간에서 약간의 동작 만 결정하는 경우 코드 중복을 줄이기 위해 하나만 유지하는 것이 좋습니다. 가능한 경우 메소드를 세 가지로 나눌 수도 있습니다. 부울 옵션에 대한 두 가지 호출 메소드와 대부분의 작업을 수행하는 하나의 호출 메소드.

예를 들면 다음과 같습니다.

private void FooInternal(bool flag)
{
  //do work
}

public void Foo1()
{
  FooInternal(true);
}

public void Foo2()
{
  FooInternal(false);
}

물론 실제로 이러한 극단 사이에는 항상 중요한 점이 있습니다. 일반적으로 올바른 느낌으로 진행하지만 코드 중복이 적은 측면에서 실수하는 것을 선호합니다.


부울 인수 만 사용하여 개인 메소드의 동작을 제어합니다 (여기 참조). 그러나 문제 : 일부 dufu가 FooInternal미래에 가시성을 높이기로 결정하면 어떻게됩니까?
ADTC

사실, 나는 다른 길을 갈 것입니다. FooInternal 내부의 코드는 4 개의 부분으로 나뉘어 야합니다. 부울 true / false 사례를 처리하기위한 2 개의 함수, 하나는 이전에 발생하는 작업과 다른 하나는 이후에 발생하는 작업을위한 것입니다. 그런 다음, 귀하 Foo1는 다음 이됩니다 : { doWork(); HandleTrueCase(); doMoreWork() }. 이상적으로, doWorkdoMoreWork기능은 각각 분할을위한 두 가지 기능이 아니라 개별 동작 (즉, 개별 기능)의 의미있는 (하나 이상의) 의미로 분류됩니다.
jpaugh

7

나는 불변 인스턴스를 반환하는 빌더 메소드를 통해 동작을 사용자 정의하는 접근법을 좋아합니다. 구아바가Splitter 사용 하는 방법 은 다음과 같습니다 .

private static final Splitter MY_SPLITTER = Splitter.on(',')
       .trimResults()
       .omitEmptyStrings();

MY_SPLITTER.split("one,,,,  two,three");

이것의 장점은 다음과 같습니다.

  • 뛰어난 가독성
  • 구성과 동작 방법의 명확한 분리
  • 대상이 무엇인지, 무엇을해야하는지,하지 말아야하는지에 대해 생각하도록하여 응집력을 향상시킵니다. 이 경우에는 Splitter입니다. someVaguelyStringRelatedOperation(List<Entity> myEntities)이라는 클래스를 넣지 Splitter않았지만 클래스 에서 정적 메소드로 넣는 것에 대해 생각할 것 StringUtils입니다.
  • 인스턴스는 사전 구성되므로 종속성을 쉽게 주입 할 수 있습니다. 클라이언트는 올바른 동작을 얻기 위해 전달할지 true또는 false메소드에 대해 걱정할 필요가 없습니다 .

1
나는 큰 구아바 애호가와 전도사로서 당신의 해결책에 부분적으로 있습니다 ...하지만 당신이 내가 정말로 찾고있는 부분을 건너 뛰기 때문에 +1을 줄 수는 없습니다. 다른 방법에 대해. 나는 그것이 당신의 설명 중 일부에 암시 적이라고 생각합니다. 따라서 명시 적으로 만들 수 있다면 질문에 더 잘 대답 할 것입니다.
Ray

나는 구성과 액톤 방법의 분리라는 아이디어를 좋아한다!
Sher10ck

구아바 도서관 링크가 끊어짐
Josh Noe

4

분명히 코드 냄새 . 에 위배되지 않는 경우 단일 책임 원칙을 다음 아마도 위반 묻지 말고, 말해 . 치다:

이 두 원칙 중 하나를 위반하지 않는 것으로 판명되면 여전히 열거 형을 사용해야합니다. 부울 플래그는 마법의 숫자에 해당하는 부울 값입니다 . foo(false)만큼 의미가 bar(42)있습니다. 열거 형은 전략 패턴에 유용하며 다른 전략을 추가 할 수있는 유연성을 제공합니다. (단지 이름을 적절하게 지정해야합니다 .)

당신의 특별한 예는 특히 나를 귀찮게합니다. 이 플래그가 왜 그렇게 많은 방법으로 전달됩니까? 이것은 매개 변수를 서브 클래스분할 해야하는 것처럼 들립니다 .


4

TL; DR : 부울 인수를 사용하지 마십시오.

아래 참조 그들이 나쁜, 그리고 방법을 대체 할 (굵게).


부울 인수는 읽기 어려우므로 유지하기가 어렵습니다. 주요 문제점은 인수의 이름이 지정된 메소드 서명을 읽을 때 일반적으로 목적이 명확하다는 것입니다. 그러나 대부분의 언어에서는 매개 변수 이름을 지정할 필요가 없습니다. 따라서 RSACryptoServiceProvider#encrypt(Byte[], Boolean)부울 매개 변수가 함수에 사용될 암호화 종류를 결정하는 위치 와 같은 안티 패턴 이 있습니다.

따라서 다음과 같은 전화를받습니다.

rsaProvider.encrypt(data, true);

여기서 독자는 단순히 지옥의 true실제 의미 를 결정하기 위해 메소드의 서명을 찾아야합니다 . 정수를 전달하는 것은 물론 나쁘다 :

rsaProvider.encrypt(data, 1);

당신에게 많은 것을 말해 줄 것입니다. 정수에 사용될 상수를 정의하더라도 함수 사용자는 단순히이를 무시하고 리터럴 값을 계속 사용할 수 있습니다.

이를 해결하는 가장 좋은 방법은 열거 형 을 사용하는 것 입니다. 당신이 열거를 전달해야하는 경우 RSAPadding이 개 값 : OAEP또는 PKCS1_V1_5당신은 즉시 코드를 읽을 수있을 것입니다 :

rsaProvider.encrypt(data, RSAPadding.OAEP);

부울은 두 개의 값만 가질 수 있습니다. 즉, 세 번째 옵션이있는 경우 서명을 리팩터링해야합니다. 이전 버전과의 호환성이 문제인 경우 일반적으로이 작업을 쉽게 수행 할 수 없으므로 다른 공용 메서드를 사용하여 모든 공용 클래스를 확장해야합니다. Microsoft RSACryptoServiceProvider#encrypt(Byte[], RSAEncryptionPadding)가 부울 대신 열거 형 (또는 열거 형을 모방하는 클래스)을 사용하는 위치 를 소개했을 때 Microsoft가 마침내 한 일입니다.

매개 변수 자체를 매개 변수화해야하는 경우 전체 오브젝트 또는 인터페이스 를 매개 변수 로 사용하는 것이 더 쉬울 수도 있습니다 . 위의 예에서 OAEP 패딩 자체는 내부적으로 사용하기 위해 해시 값으로 매개 변수화 될 수 있습니다. 이제 6 개의 SHA-2 해시 알고리즘과 4 개의 SHA-3 해시 알고리즘이 있으므로 매개 변수 대신 단일 열거 만 사용하면 열거 값의 수가 폭발 할 수 있습니다 (이것은 Microsoft가 다음에 알아낼 것입니다) ).


부울 매개 변수는 메소드 또는 클래스가 제대로 설계되지 않았 음을 나타낼 수도 있습니다. 위의 예와 같이 .NET 이외의 암호화 라이브러리는 메소드 서명에 패딩 플래그를 전혀 사용하지 않습니다.


내가 좋아하는 거의 모든 소프트웨어 전문가는 부울 인수에 대해 경고합니다. 예를 들어 Joshua Bloch는 "Effective Java"라는 저명한 저서에서 이에 대해 경고합니다. 일반적으로 단순히 사용해서는 안됩니다. 이해하기 쉬운 하나의 매개 변수가있는 경우 사용할 수 있다고 주장 할 수 있습니다. 그러나 그렇다하더라도 : Bit.set(boolean)아마 더 나은 사용하여 구현 하는 두 가지 방법을 : Bit.set()Bit.unset().


코드를 직접 리팩토링 할 수없는 경우 상수정의 하여 최소한 더 읽기 쉽게 만들 수 있습니다.

const boolean ENCRYPT = true;
const boolean DECRYPT = false;

...

cipher.init(key, ENCRYPT);

다음보다 훨씬 더 읽기 쉽습니다.

cipher.init(key, true);

오히려 원하는 경우에도 :

cipher.initForEncryption(key);
cipher.initForDecryption(key);

대신에.


3

아무도 named-parameters를 언급하지 않은 것에 놀랐습니다 .

부울 플래그에서 볼 수있는 문제는 가독성을 떨어 뜨린다는 것입니다. 예를 들어, 어떤 일이 수행 true

myObject.UpdateTimestamps(true);

하다? 나도 몰라 그러나 어떻습니까 :

myObject.UpdateTimestamps(saveChanges: true);

이제 우리가 전달하는 매개 변수가 무엇을 의미하는지 분명합니다. 변경 사항을 저장하도록 함수에 지시합니다. 이 경우 클래스가 공용이 아닌 경우 부울 매개 변수가 적합하다고 생각합니다.


물론 클래스 사용자가 명명 된 매개 변수를 사용하도록 강요 할 수는 없습니다. 이러한 이유로 enum기본 사례가 얼마나 일반적인 지에 따라 일반적으로 한두 가지 방법이 바람직합니다. 이것이 바로 .Net이하는 일입니다.

//Enum used
double a = Math.Round(b, MidpointRounding.AwayFromZero);

//Two separate methods used
IEnumerable<Stuff> ascendingList = c.OrderBy(o => o.Key);
IEnumerable<Stuff> descendingList = c.OrderByDescending(o => o.Key); 

1
문제는 행동 결정 플래그보다 바람직한 것에 관한 것이 아니라, 그러한 플래그가 냄새인지, 그리고 그렇다면, 왜
Ray

2
@ 레이 :이 두 질문 사이에는 차이가 없습니다. 명명 된 매개 변수의 사용을 강제 할 수있는 언어 또는 명명 된 매개 변수가 항상 사용되도록 보장 할 수있는 경우 (예 : 개인용 메소드) 부울 매개 변수가 적합합니다. 명명 된 매개 변수를 언어 (C #)로 적용 할 수없고 클래스가 공용 API의 일부이거나 해당 언어가 명명 된 매개 변수 (C ++)를 지원하지 않아 코드 myFunction(true)가 작성 될 수있는 경우 코드입니다. 냄새.
BlueRaja-대니 Pflughoeft

명명 된 매개 변수 접근 방식이 훨씬 더 잘못되었습니다. 이름이 없으면 API 문서를 읽어야합니다. 이름을 사용하면 필요하지 않다고 생각하지만 매개 변수가 잘못되었을 수 있습니다. 예를 들어, (모든) 변경 사항을 저장하는 데 사용될 수 있었지만 나중에는 변경 사항 만 저장 하기 위해 (일부 큰 값의 경우) 구현이 약간 변경되었습니다.
Ingo

@Ingo 동의하지 않습니다. 그것은 일반적인 프로그래밍 문제입니다. 다른 SaveBig이름을 쉽게 정의 할 수 있습니다 . 모든 코드를 조일 수 있습니다. 이러한 종류의 조임은 명명 된 매개 변수에만 국한되지 않습니다.
Maarten Bodewes

1
@ 잉고 : 당신이 바보에 둘러싸여 있다면, 당신은 가서 다른 곳에서 일자리를 찾을 수 있습니다. 그런 종류의 것은 코드 리뷰가 있습니다.
gnasher729

1

나는 무엇이 잘못되었는지 분명히 말할 수 없습니다.

코드 냄새처럼 보이거나 코드 냄새가 나고 코드 냄새가 나면 코드 냄새 일 수 있습니다.

당신이하고 싶은 것은 :

1) 부작용이있는 방법을 피하십시오.

2) 중앙의 공식적인 상태 머신 (이와 같은 )으로 필요한 상태를 처리하십시오 .


1

부울 매개 변수를 사용하여 성능을 결정하지 않기위한 모든 문제에 동의합니다. 개선, 가독성, 신뢰성, 복잡성 감소, 불량 캡슐화 및 응집으로 인한 위험 감소 및 유지 관리가 가능한 총 소유 비용 절감.

70 년대 중반에 SCADA (감독 제어 및 데이터 수집)라고하는 하드웨어 설계를 시작했으며 매크로 리모콘을 실행하고 고속 데이터를 수집하는 EPROM에 기계 코드가있는 미세 조정 된 하드웨어였습니다.

이 로직을 Mealey & Moore 머신 이라고 하며 , 이를 현재 유한 상태 머신이라고 합니다. 실행 시간이 한정된 실시간 머신이 아니라면 목적에 맞게 바로 가기를 수행해야하는 경우가 아니라면 위와 동일한 규칙으로 수행해야합니다.

데이터는 동 기적이지만 명령은 비동기 적이며 명령 논리는 메모리가없는 부울 논리를 따르지만 이전, 현재 및 원하는 다음 상태의 메모리를 기반으로하는 순차적 명령이 있습니다. 가장 효율적인 기계 언어 (64kB 만)로 작동하려면 휴리스틱 IBM HIPO 방식으로 모든 프로세스를 정의하기 위해 각별한주의를 기울였습니다. 때로는 부울 변수를 전달하고 색인 분기를 수행하는 것을 의미했습니다.

그러나 메모리가 많고 OOK가 쉬워 짐에 따라 Encapsulation은 오늘날 필수 요소이지만 실시간 바이트 코드 및 SCADA 머신 코드로 계산 될 때의 패널티입니다.


0

반드시 틀린 것은 아니지만 "사용자"의 일부 속성에 따른 구체적인 동작 예제에서는 플래그가 아닌 사용자에 대한 참조를 전달합니다.

이것은 여러 가지 방법으로 명확하고 도움이됩니다.

호출 명령문을 읽는 사람은 결과가 사용자에 따라 변경됨을 인식 할 것입니다.

궁극적으로 호출되는 기능에서는 모든 사용자 속성에 액세스 할 수 있으므로보다 복잡한 비즈니스 규칙을 쉽게 구현할 수 있습니다.

"체인"의 한 기능 / 방법이 사용자 속성에 따라 다른 경우, 사용자 속성에 대한 유사한 종속성이 "체인"의 다른 방법 중 일부에 도입 될 가능성이 높습니다.


0

대부분의 경우이 잘못된 코딩을 고려합니다. 그러나 이것이 좋은 방법 일 수있는 두 가지 경우를 생각할 수 있습니다. 왜 나쁜지에 대해 이미 많은 답변이 있으므로 좋을 때 두 번 제안합니다.

첫 번째는 시퀀스의 각 호출이 그 자체로 의미가있는 경우입니다. 호출 코드 true에서 false로 또는 false에서 true로 변경 되거나 호출 된 메소드 부울 매개 변수를 전달하지 않고 직접 사용하도록 변경 수 있는지 여부는 의미 있습니다. 한 번에 10 번의 이러한 호출 가능성은 적지 만 발생할 수 있으며, 그렇게하면 좋은 프로그래밍 연습이 될 것입니다.

두 번째 경우는 부울을 처리한다는 점에서 약간의 확장입니다. 그러나 프로그램에 여러 스레드 또는 이벤트가있는 경우 매개 변수를 전달하는 것이 스레드 / 이벤트 특정 데이터를 추적하는 가장 간단한 방법입니다. 예를 들어, 프로그램은 둘 이상의 소켓에서 입력을받을 수 있습니다. 한 소켓에서 실행되는 코드는 경고 메시지를 생성해야 할 수도 있고 다른 소켓에서 실행되는 코드는 그렇지 않을 수도 있습니다. 그런 다음 (높은 수준의) 부울 세트가 경고 메시지가 생성 될 수있는 위치로 많은 메소드 호출을 통과하는 것이 합리적입니다. 여러 스레드 또는 인터리브 된 이벤트에는 각각 고유 한 값이 필요하기 때문에 모든 종류의 전역에 데이터를 저장할 수 없습니다 (큰 어려움 제외).

확실히 후자의 경우 내용이 부울 인 클래스 / 구조를 만들고 대신 전달 합니다 . 예를 들어 경고 메시지를 보낼 위치 와 같이 너무 오래 전에 다른 필드가 필요할 것이라고 확신 합니다.


열거 형 WARN, SILENT는 클래스 / 구조를 쓸 때 (즉, context ) 더 이해가됩니다 . 또는 실제로 로깅을 외부에서 구성하기 만하면됩니다.
Maarten Bodewes

-1

상황이 중요합니다. 이러한 방법은 iOS에서 일반적입니다. 자주 사용되는 예로 UINavigationController가 메소드를 제공 -pushViewController:animated:하며 animated매개 변수는 BOOL입니다. 이 방법은 본질적으로 동일한 기능을 수행하지만 YES를 전달하면 한 뷰 컨트롤러에서 다른 것으로 변경되고 NO를 전달하면 애니메이션에 적용되지 않습니다. 이것은 전적으로 합리적입니다. 애니메이션을 사용할지 여부를 결정할 수 있도록이 방법 대신 두 가지 방법을 제공해야하는 것은 어리석은 일입니다.

Objective-C에서 이러한 종류의 것을 정당화하는 것이 더 쉬울 수 있습니다. 여기서 메소드 이름 지정 구문은 C 및 Java와 같은 언어에서보다 각 매개 변수에 대해 더 많은 컨텍스트를 제공합니다. 그럼에도 불구하고 단일 매개 변수를 사용하는 메서드는 쉽게 부울을 취하고 여전히 이해가 될 수 있다고 생각합니다.

file.saveWithEncryption(false);    // is there any doubt about the meaning of 'false' here?

21
실제로 나는 예제 false에서 무엇을 의미 하는지 전혀 모른다 file.saveWithEncryption. 암호화없이 저장한다는 의미입니까? 그렇다면, 왜 세계에서이 방법이 이름에 "암호화"가 있습니까? 와 같은 방법을 이해할 수는 save(boolean withEncryption)있지만, 볼 때 file.save(false)매개 변수가 암호화 유무에 관계없이 있음을 한눈에 알 수는 없습니다. 사실, 이것이 열거 형 사용에 관한 James Youngman의 첫 번째 요점이라고 생각합니다.
Ray

6
다른 가설은 false같은 이름의 기존 파일을 덮어 쓰지 않는다는 것입니다. 내가 아는 좋은 예이지만 기능에 대한 설명서 (또는 코드)를 확인해야합니다.
James Youngman

5
saveWithEncryption암호화와 함께 저장되지 않는 방법 은 버그입니다. 아마도 Java file.encrypt().save()와 비슷 해야합니다 new EncryptingOutputStream(new FileOutputStream(...)).write(file).
Christoffer Hammarström

2
실제로, 코드가 아닌 다른 것을 수행하는 코드는 작동하지 않으므로 버그입니다. saveWithEncryption(boolean)때로는 암호화하지 않고 저장 하는 방식 과 달리 암호화하지 않고 저장 되는 방법 을 만들지 않습니다 saveWithoutEncryption(boolean).
Christoffer Hammarström

2
이 방법은 분명히 "여기서 '거짓'의 의미에 대한 의심이 있기 때문에"나쁘다. 어쨌든, 나는 처음에 그런 방법을 쓰지 않을 것입니다. 저장 및 암호화는 별도의 작업이므로 방법이 한 가지 작업을 수행하고 잘 수행해야합니다. 더 나은 예제를 보려면 이전 주석을 참조하십시오.
Christoffer Hammarström
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.