[oop] 보호 된 멤버 변수를 사용해야합니까?

보호 된 멤버 변수를 사용해야합니까? 장점은 무엇이며 이로 인해 어떤 문제가 발생할 수 있습니까?



답변

보호 된 멤버 변수를 사용해야합니까?

상태를 숨기는 것에 대해 얼마나 까다로운 지에 따라 다릅니다.

  • 내부 상태의 유출을 원하지 않는 경우 모든 멤버 변수를 private으로 선언하는 것이 좋습니다.
  • 하위 클래스가 내부 상태에 액세스 할 수 있다는 사실에 관심이 없다면 protected로 충분합니다.

개발자가 함께 와서 클래스를 하위 클래스로 지정하면 완전히 이해하지 못하기 때문에 엉망이 될 수 있습니다. 공용 인터페이스가 아닌 개인 멤버를 사용하면 작업이 수행되는 방식에 대한 구현 특정 세부 사항을 볼 수 없으므로 나중에 변경할 수있는 유연성을 제공합니다.


답변

요즘 일반적인 느낌은 파생 클래스와 그 기반 사이에 과도한 결합을 유발한다는 것입니다.

보호 된 메서드 / 속성에 비해 특별한 이점이 없으며 (한때 약간의 성능 이점이있을 수 있음), 매우 깊은 상속이 유행하던 시대에 더 많이 사용되었지만 현재는 그렇지 않습니다.


답변

일반적으로 의도적으로 공개 된 것으로 간주되지 않은 내용은 비공개로 설정합니다.

파생 클래스에서 해당 개인 변수 또는 메서드에 액세스해야하는 상황이 발생하면 개인에서 보호로 변경합니다.

이것은 거의 발생하지 않습니다. 대부분의 상황을 모델링하는 데 특히 좋은 방법이 아니기 때문에 저는 상속의 팬이 아닙니다. 어쨌든 계속해서 걱정하지 마십시오.

나는 이것이 대부분의 개발자들에게 괜찮다고 말하고 싶습니다 (그리고 아마도 그것에 대해 최선의 방법 일 것입니다).

문제의 간단한 사실은 다른 개발자가 1 년 후에 와서 개인 멤버 변수에 대한 액세스가 필요하다고 결정하면 코드를 편집하고 보호 된 것으로 변경하고 비즈니스를 계속할 것입니다.

이에 대한 유일한 예외는 바이너리 dll을 블랙 박스 형태로 제 3 자에게 배송하는 사업에있는 경우입니다. 이것은 기본적으로 Microsoft, ‘Custom DataGrid Control’공급 업체 및 확장 성 라이브러리와 함께 제공되는 몇 가지 다른 대형 앱으로 구성됩니다. 당신이 그 범주에 속하지 않는 한, 이런 종류의 것에 대해 걱정하는 데 시간 / 노력을 소비 할 가치가 없습니다.


답변

나에게 중요한 문제는 일단 변수를 보호 하면 하위 클래스가 항상 범위를 벗어날 수 있기 때문에 클래스의 어떤 메서드도 범위 내에있는 값에 의존 하도록 허용 할 수 없다는 것입니다.

예를 들어 렌더링 가능한 객체의 너비와 높이를 정의하는 클래스가 있고 해당 변수를 보호하는 경우 가로 세로 비율 (예 : 가로 세로 비율)에 대한 가정을 할 수 없습니다.

비판적으로 저는 코드가 라이브러리로 출시 된 순간부터 이러한 가정을 할 수 없습니다. 종횡비를 유지하기 위해 setter를 업데이트하더라도 변수가 setter를 통해 설정되거나 기존 코드의 게터.

내 클래스의 어떤 하위 클래스도 변수 값을 적용 할 수 없기 때문에 보장하도록 선택할 수 없습니다. 그 자신의 서브 클래스의 전체 지점의 경우에도 .

예로서:

  • 너비와 높이가 보호 된 변수로 저장되는 사각형 클래스가 있습니다.
  • 내 컨텍스트 내에서 명백한 하위 클래스는 “DisplayedRectangle”클래스입니다. 유일한 차이점은 그래픽 디스플레이에 대해 너비와 높이를 유효한 값으로 제한한다는 것입니다.
  • 하지만 지금은 불가능합니다. 내 DisplayedRectangle 클래스 해당 값을 진정으로 제한 할 수 없기 때문에 하위 클래스가 DisplayedRectangle로 처리되는 동안 값을 직접 재정의 할 수 있기 때문입니다.

변수를 비공개로 제한하면 setter 또는 getter를 통해 원하는 동작을 적용 할 수 있습니다.


답변

일반적으로 보호 된 멤버 변수를 사용하는 코드를 완전히 제어 할 수있는 드문 경우로 유지합니다. 공용 API를 만드는 경우 절대로하지 않습니다. 아래에서 멤버 변수를 개체의 “속성”이라고합니다.

다음 은 private-with-accessor가 아닌 멤버 변수를 보호 한 후에 슈퍼 클래스가 할 수없는 작업입니다.

  1. 속성을 읽을 때 즉시 값을 느리게 만듭니다. 보호 된 getter 메서드를 추가하면 값을 느리게 생성하고 다시 전달할 수 있습니다.

  2. 속성이 수정되거나 삭제 된시기를 알 수 있습니다. 수퍼 클래스가 해당 변수의 상태에 대해 가정 할 때 버그가 발생할 수 있습니다. 변수에 대해 보호 된 setter 메서드를 만들면 해당 제어가 유지됩니다.

  3. 변수를 읽거나 쓸 때 중단 점을 설정하거나 디버그 출력을 추가합니다.

  4. 해당 멤버 변수를 사용할 수있는 모든 코드를 검색하지 않고 이름을 바꿉니다.

일반적으로 보호 된 멤버 변수를 만들 것을 권장하는 드문 경우라고 생각합니다. 몇 시간 후에 보호 된 변수를 수정 한 다른 코드에서 버그를 추적하는 것보다 게터 / 세터를 통해 속성을 노출하는 데 몇 분을 보내는 것이 좋습니다. 뿐만 아니라 종속 코드를 손상시키지 않고 향후 기능 (예 : 지연로드)을 추가하지 않도록 보장합니다. 지금하는 것보다 늦게하는 것이 더 어렵습니다.


답변

디자인 수준에서는 보호 된 속성을 사용하는 것이 적절할 수 있지만 구현을 위해 접근 자 / 변이 자 메서드가 아닌 보호 된 멤버 변수에 매핑하는 이점이 없습니다.

보호 된 멤버 변수는 기본 클래스 클래스의 내부 상태에 대한 클라이언트 코드 (하위 클래스) 액세스를 효과적으로 허용하기 때문에 상당한 단점이 있습니다. 이것은 기본 클래스가 불변성을 효과적으로 유지하는 것을 방지합니다.

같은 이유로 보호 된 멤버 변수는 상수가 보장되거나 단일 스레드로 제한되지 않는 한 안전한 다중 스레드 코드 작성을 훨씬 더 어렵게 만듭니다.

접근 자 / 변이 자 메서드는 유지 관리 중에 훨씬 더 많은 API 안정성과 구현 유연성을 제공합니다.

또한 OO 순수 주의자라면 객체는 상태를 읽거나 설정하지 않고 메시지를 보내 공동 작업 / 통신합니다.

그 대가로 그들은 거의 장점을 제공하지 않습니다. 다른 사람의 코드에서 반드시 제거하지는 않지만 직접 사용하지는 않습니다.


답변

대부분의 경우 protected를 사용하는 것은 잘못 설계된 파생 클래스에 의해 잘 분해 될 수있는 클래스의 캡슐화를 다소 깨뜨리기 때문에 위험합니다.

하지만 좋은 예가 하나 있습니다. 일종의 일반 컨테이너를 사용할 수 있다고 가정 해 보겠습니다. 내부 구현과 내부 접근자가 있습니다. 그러나 데이터에 대한 공개 액세스 권한을지도, hash_map, 벡터와 같이 3 개 이상 제공해야합니다. 그러면 다음과 같은 것이 있습니다.

template <typename T, typename TContainer>
class Base
{
   // etc.
   protected
   TContainer container ;
}

template <typename Key, typename T>
class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }

template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }

template <typename T>
class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }

나는 이런 종류의 코드를 한 달 전에 사용했습니다 (따라서 코드는 메모리에서 가져온 것입니다). 몇 가지 생각을 한 후, 제네릭 Base 컨테이너는 추상 클래스 여야한다고 생각합니다. 비록 그것이 꽤 잘 살 수 있더라도 Base를 직접 사용하는 것은 매우 고통 스러울 것이기 때문에 금지되어야합니다.

요약 따라서 파생 클래스에서 사용하는 데이터를 보호했습니다. 그럼에도 불구하고 우리는 Base 클래스가 추상적이어야한다는 사실을 고려해야합니다.