Clean Code가 보호 변수를 피하도록 제안하는 이유는 무엇입니까?


168

Clean Code 는 "서식 지정"장의 "수직 거리"섹션에서 보호 된 변수를 피할 것을 제안합니다.

밀접하게 관련된 개념은 서로 수직으로 유지해야합니다. 분명히이 규칙은 별도의 파일에 속하는 개념에는 적용되지 않습니다. 그러나 매우 적절한 이유가 없다면 밀접하게 관련된 개념을 다른 파일로 분리해서는 안됩니다. 실제로 이것은 보호 된 변수를 피해야하는 이유 중 하나입니다 .

추론은 무엇입니까?


7
필요 하다고 생각되는 예를 보여 주십시오
gnat

63
나는 그 책에서 복음으로 많이 쓰지 않을 것입니다.
장비

40
@Rig 당신은 이런 부분들과 같은 의견으로 신성 모독에 접해있다.
Ryathal

40
난 괜찮아 나는 책을 읽었고 그것에 대한 내 의견을 가질 수 있습니다.
장비

10
나는이 책을 읽었 으며 거기에있는 내용의 대부분 에 동의하지만 복음이라고 생각하지는 않습니다. 나는 모든 상황이 다르다고 생각합니다. 그리고이 책에 표현 된 정확한 지침을 고수하는 것은 많은 프로젝트에서 상당히 어렵습니다.
Simon Whitehead

답변:


277

다음과 같은 이유로 보호 된 변수를 피해야합니다.

  1. 그들은 YAGNI 문제 를 일으키는 경향이 있습니다. 보호 된 멤버와 실제로 작업을 수행하는 하위 클래스가 없으면 비공개로 만듭니다.
  2. 그들은 LSP 문제 를 일으키는 경향이 있습니다. 보호 변수는 일반적으로 변수와 관련된 본질적인 불일치가 있습니다 (또는 공개 될 수 있음). 그런 다음 상속자는 사람들이 실수하거나 고의적으로 위반할 수있는 속성을 유지해야합니다.
  3. 그들은 OCP 를 위반하는 경향이 있습니다. 기본 클래스가 보호 된 멤버에 대해 너무 많은 가정을하거나 상속자가 클래스의 동작에 너무 유연하면 기본 클래스의 동작이 해당 확장에 의해 수정 될 수 있습니다.
  4. 그것들은 구성보다는 확장을위한 상속으로 이어지는 경향이 있습니다. 이것은 더 긴밀한 결합, 더 많은 SRP 위반 , 더 어려운 테스트 및 '상속보다 선호되는 구성'토론에 속하는 많은 다른 것들을 야기 하는 경향이 있습니다 .

그러나 보시다시피,이 모든 것들이 '가장 경향이 있습니다'. 때때로 보호 된 구성원이 가장 우아한 솔루션입니다. 그리고 보호 기능 에는 이러한 문제가 적은 경향이 있습니다. 그러나 그것들을 조심스럽게 다루게하는 많은 것들이 있습니다. 이런 종류의 관리가 필요한 것은 사람들이 실수를하거나 프로그래밍 세계에서 버그와 디자인 문제를 의미합니다.


여러 가지 좋은 이유에 감사드립니다. 그러나이 책은 형식과 수직 거리와 관련하여 구체적으로 언급했으며, 내가 궁금한 부분입니다.
Matsemann

2
누군가가 같은 클래스가 아닌 클래스 간에 보호 된 멤버를 언급 할 때 Bob 아저씨가 수직 거리를 참조하는 것 같습니다 .
Robert Harvey

5
@ Matsemann-물론. 이 책을 정확하게 기억한다면이 섹션은 코드의 가독성과 발견 가능성에 중점을 둡니다. 변수와 함수가 개념에서 작동하면 코드와 비슷해야합니다. 보호 된 멤버는 파생 된 형식에 의해 사용되며, 완전히 다른 파일에 있기 때문에 보호 된 멤버와 반드시 가까울 수는 없습니다.
Telastyn

2
@ steve314 기본 클래스와 모든 상속자가 하나의 파일에만 살 수있는 시나리오를 상상할 수 없습니다 . 예를 들지 않으면 나는 그것이 상속의 남용이나 열악한 추상화라고 생각하는 경향이있다.
Telastyn

3
YAGNI = 당신 아닌가요 후끈는 LSP =리스 코프 치환 원칙 OCP = 열기 / 닫기 원리 SRP = 단일 책임 원칙이 필요
브라이언 글레이저

54

더 작은 규모로 지구본을 피하는 것과 같은 이유입니다. 변수가 읽히거나 나 빠지고, 쓰여지고, 일관되게 사용하기가 어려운 모든 곳을 찾기가 어렵 기 때문입니다. 사용량이 작성되는 등, 제한하는 경우 하나 개의 파생 클래스에서 명백한 장소와 읽을 하나 개의 기본 클래스에서 명백한 장소, 다음 보호 된 변수는 코드가 더 명확하고 간결하게 만들 수 있습니다. 여러 파일에서 변수 willy-nilly를 읽고 쓰려는 유혹을 받으면 캡슐화하는 것이 좋습니다.


26

나는이 책을 읽지 않았지만 밥 아저씨가 의미 한 바를 찌를 수있다.

당신이 넣으면 protected뭔가, 그 클래스가 그것을 상속 할 수 있다는 것을 의미한다. 그러나 멤버 변수는 포함 된 클래스에 속해야합니다. 이것은 기본 캡슐화의 일부입니다. 퍼팅 protected지금 파생 클래스는 기본 클래스의 구현 세부 사항에 액세스 할 수 있기 때문에 멤버 변수 휴식 캡슐화에. public일반 클래스 에서 변수를 만들 때 발생하는 것과 동일한 문제입니다 .

문제를 해결하기 위해 다음과 같이 보호 된 속성에 변수를 캡슐화 할 수 있습니다.

protected string Name
{
    get { return name; }
    private set { name = value; }
}

이를 통해 name기본 클래스의 구현 세부 사항을 노출시키지 않고 생성자 인수를 사용하여 파생 클래스에서 안전하게 설정할 수 있습니다.


보호 된 setter를 사용하여 공용 특성을 작성했습니다. 보호 된 필드는 개인 설정 기가있는 보호 된 속성으로 해결해야합니다.
Andy

2
+1하세요. 세터도 보호 될 수 있다고 생각하지만 새 값이 유효한지 여부를 기반으로 할 수있는 포인트 ID.
Andy

반대 관점을 제공하기 위해 모든 변수를 기본 클래스로 보호합니다. 공용 인터페이스를 통해서만 액세스해야하는 경우 파생 클래스가 아니어야하며 단지 기본 클래스 개체 만 포함해야합니다. 그러나 나는 또한 2 개 이상의 심층 계층을 피한다
Martin Beckett

2
회원 과 는 차이가 있습니다. 퍼블릭 멤버를 노출하는 경우 인스턴스를 참조로 수신하고 해당 멤버를 사용할 것으로 예상 되는 코드 로 인스턴스를 전달할 수 있으므로 모든 파생 형식에 동일한 멤버가 동일한 방식으로 작동해야 함을 의미합니다 . 반대로, 멤버를 노출하는 경우의 에서 해당 멤버에 액세스 할 것으로 예상 할 수 있지만 a 이외의 다른 것은 될 수 없습니다 . protectedpublicBaseTypeDerivedTypeBaseTypeBaseTypeprotectedDerivedTypebasebaseBaseType
supercat

18

Bob 아저씨의 주장은 주로 거리 중 하나입니다. 클래스에 중요한 개념이 있다면 개념을 해당 파일의 클래스와 함께 묶습니다. 디스크의 두 파일에서 분리되지 않습니다.

보호 된 멤버 변수는 두 곳에 흩어져 있으며 다소 마술처럼 보입니다. 이 변수를 참조, 아직 어디 ... 여기에 정의되지 않은 있다 가 정의? 따라서 사냥이 시작됩니다. protected그의 주장을 완전히 피하는 것이 좋습니다.

이제 나는 규칙이 항상 편지에 순종해야한다고 생각하지 않습니다 (예 : 너는 사용하지 않아야 함 protected). 상기 봐 정신 ... 그가에서지고 무엇을 하나 개의 파일로 함께 관련 일을 번들 - 그렇게 기술과 기능을 프로그래밍 사용합니다. 나는 당신이 지나치게 분석하지 말고 이것에 대한 세부 사항에 얽 히지 않는 것이 좋습니다.


9

이미 여기에 아주 좋은 답변이 있습니다. 그러나 한 가지 추가해야 할 사항은 다음과 같습니다.

대부분의 현대 OO 언어에서는 각 클래스를 자체 파일에 넣는 것이 좋습니다 (Java AFAIK에서는 필수).

따라서 소스 클래스에서 변수 정의에 "수직으로 가까운"기본 클래스의 보호 된 변수에 액세스하는 파생 클래스의 함수를 유지하는 것은 사실상 불가능합니다. 그러나 보호 된 변수는 내부 용으로 만들어져있어 기능을 자주 수행하는 기능이 종종 "밀접한 관련 개념"이므로이를 유지해야합니다.


1
스위프트에는 없습니다. 흥미롭게도 Swift는 "보호 된"기능은 없지만 "fileprivate"는 "protected"와 C ++ "friend"를 대체합니다.
gnasher729

@ gnasher729-물론, 스위프트는 유산에 스몰 토크가 많습니다. 스몰 토크에는 개인 변수와 공용 메소드 이외의 것이 없습니다. Smalltalk의 private은 private을 의미합니다. 같은 클래스의 두 객체는 ​​서로의 변수에 액세스 할 수 없습니다.
Jules

4

기본 개념은 protected로 선언 된 "필드"(인스턴스 레벨 변수)가 원하는 것보다 더 가시적이며 원하는 것보다 덜 "보호 된"것입니다. C / C ++ / Java / C #에는 "동일한 어셈블리 내에서 자식 클래스 만 액세스 할 수있는"액세스 수정자가 없으므로 어셈블리의 필드에 액세스 할 수있는 자체 자식을 정의 할 수 있지만 다른 어셈블리에서 작성된 하위 항목에 동일한 액세스를 허용하지 않습니다. C #에는 내부 및 보호 수정자가 있지만 이들을 결합하면 "내부 및 보호"가 아닌 "내부 또는 보호"액세스가 가능합니다. 따라서 보호 대상 필드는 그 아이를 쓰든 다른 사람을 쓰든 모든 아이가 액세스 할 수 있습니다. 따라서 보호자는 해커에게 열린 문입니다.

또한 정의에 따라 필드는 필드를 변경하는 데 고유 한 검증이 거의 없습니다. C #에서는 하나의 읽기 전용을 만들 수 있습니다. 이렇게하면 값 유형을 효과적으로 일정하게 유지하고 참조 유형을 다시 초기화 할 수는 없지만 여전히 변경 가능합니다. 따라서 보호 대상이더라도 자녀 (신뢰할 수없는)는이 필드에 액세스 할 수 있으며이 필드를 유효하지 않은 것으로 설정하여 오브젝트의 상태가 일치하지 않도록합니다 (피해야 할 것).

허용되는 필드 작업 방법은 필드를 비공개로 설정하고 속성 및 / 또는 getter 및 setter 메서드를 사용하여 필드에 액세스하는 것입니다. 클래스의 모든 소비자가 가치를 필요로하는 경우 게터를 공개적으로 공개하십시오. 어린이 만 필요하면 게터를 보호하십시오.

질문에 답하는 또 다른 접근법은 스스로에게 물어 보는 것입니다. 자식 메소드의 코드가 내 상태 데이터를 직접 수정하는 기능이 필요한 이유는 무엇입니까? 그 코드에 대해 무엇을 말합니까? 그것은 얼굴의 "수직 거리"주장입니다. 부모 상태를 직접 변경 해야하는 코드가 자식에 있으면 해당 코드가 처음에 부모에 속해야합니까?


4

흥미로운 토론입니다.

솔직하게 말하면 훌륭한 도구를 사용하면 이러한 "수직"문제를 줄일 수 있습니다. 사실 제 생각에 "파일"이라는 개념은 실제로 소프트웨어 개발을 방해하는 것입니다. Light Table 프로젝트가 진행중인 것입니다 (http://www.chris-granger.com/2012/04/12/light- 표 --- 새로운 아이디어 개념 /).

그림에서 파일을 꺼내면 보호 범위가 훨씬 더 매력적입니다. 상속 개념이 클래스의 원래 개념을 단순히 확장하는 SmallTalk와 같은 언어에서 보호 된 개념이 가장 명확 해집니다. 부모 클래스가하는 모든 것을해야합니다.

내 생각에 클래스 계층은 가능한 한 얕아 야하며 대부분의 확장은 구성에서 나옵니다. 이러한 모델에서는 개인 변수가 보호 되지 않는 이유가 없습니다 . 확장 클래스가 부모 클래스의 모든 동작을 포함하여 확장을 나타내야한다면 (필자가 믿기 때문에 개인 메소드가 본질적으로 나쁘다고 생각합니다) 확장 클래스가 왜 그러한 데이터의 저장을 나타내지 않아야합니까?

다른 방법으로-개인 변수가 보호 변수보다 더 적합하다고 생각되는 경우 상속은 처음에는 문제의 해결책이 아닙니다.


0

그것이 말했듯이, 이것은 논리적으로 연결된 코드가 물리적으로 연결된 엔티티 (파일, 패키지 및 기타) 안에 있어야하는 이유 중 하나 일뿐입니다. 더 작은 규모에서 이것은 UI를 표시하는 클래스를 사용하여 패키지 내에 DB 쿼리를 만드는 클래스를 넣지 않는 이유와 동일합니다.

그러나 보호 된 변수가 권장되지 않는 주된 이유는 캡슐화를 중단하는 경향이 있기 때문입니다. 변수와 메소드는 가능한 한 제한된 가시성을 가져야합니다. 자세한 내용은 Joshua Bloch의 효과적인 Java , 항목 13- "클래스 및 멤버의 액세스 가능성 최소화"를 참조하십시오 .

그러나이 광고를 모두 길드 라인으로 삼아서는 안됩니다. 보호 된 변수가 매우 나쁘면 처음에는 언어 내부에 배치되지 않았을 것입니다. 보호 필드에 적합한 장소는 테스트 프레임 워크의 기본 클래스 내에 있습니다 (통합 테스트 클래스는이를 확장합니다).


1
언어가 허용하기 때문에 가능하다고 가정하지 마십시오. 나는 보호 된 들판을 허용해야 할 신의 이유가 실제로 있는지 확실하지 않다. 실제로 고용주에게 허용하지 않습니다.
Andy

2
@ 앤디 당신은 내가 한 말을 읽지 않았습니다. 보호 된 필드는 권장되지 않으며 이에 대한 이유가 있다고 말했습니다. 보호 된 변수가 필요한 시나리오가 분명히 있습니다. 서브 클래스에서만 일정한 최종 값을 원할 수 있습니다.
m3th0dman

변수와 필드를 상호 교환하여 사용하고 보호 된 consts와 같은 것을 예외로 간주한다고 명시하기 때문에 게시물의 일부를 다시 말하고 싶을 수 있습니다.
Andy

3
@Andy 보호 된 변수를 언급했기 때문에 로컬 또는 매개 변수가 아니라 필드에 대해 이야기하고 있음이 분명합니다. 상수가 아닌 보호 변수가 유용한 시나리오도 언급했습니다. 기업이 프로그래머가 코드를 작성하는 방식을 시행하는 것은 잘못입니다.
m3th0dman

1
그것이 분명하다면 나는 당신을 혼란스럽고 다운 보트하지 않았을 것입니다. StyleCop, FxCop을 실행하고 단위 테스트 및 코드 검토가 필요하다는 결정과 마찬가지로 수석 개발자가이 규칙을 만들었습니다.
Andy

0

JUnit 테스트에 보호 변수를 사용하면 유용 할 수 있습니다. 비공개로 설정하면 테스트 중에 반영없이 액세스 할 수 없습니다. 많은 상태 변경을 통해 복잡한 프로세스를 테스트하는 경우 유용 할 수 있습니다.

보호 된 getter 메소드를 사용하여 비공개로 만들 수 있지만 JUnit 테스트 중에 변수를 외부에서만 사용하려는 경우 더 나은 솔루션인지 확실하지 않습니다.


-1

그렇다. 나는이 책이 피해야 할 것으로 모든 비 개인 변수를 논의한다는 점을 감안할 때 다소 이상한 점에 동의한다.

protected modifier의 문제는 멤버가 서브 클래스에 노출 될뿐만 아니라 전체 패키지에도 표시된다는 것입니다. 밥 삼촌이 여기서 예외로 삼고있는 것은이 두 가지 측면이라고 생각합니다. 물론, 항상 보호 된 방법과 보호 된 변수 자격을 갖출 수는 없습니다.

더 흥미로운 접근 방식은 모든 것을 공개하는 것이므로 작은 클래스를 강요하여 전체 수직 거리 측정법을 다소 무질서하게 만듭니다.

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