[oop] 상속 vs. 집계 [닫기]

객체 지향 시스템에서 코드를 가장 잘 확장, 향상 및 재사용하는 방법에 대한 두 가지 생각 학교가 있습니다.

  1. 상속 : 서브 클래스를 생성하여 클래스의 기능을 확장합니다. 서브 클래스에서 수퍼 클래스 멤버를 대체하여 새로운 기능을 제공하십시오. 수퍼 클래스가 특정 인터페이스를 원하지만 구현에 대해 무시할 때 서브 클래스가 “공란을 채우도록”추상 / 가상 메소드를 작성하십시오.

  2. 집계 : 다른 클래스를 가져 와서 새 클래스로 결합하여 새로운 기능을 만듭니다. 다른 코드와의 상호 운용성을 위해이 새로운 클래스에 공통 인터페이스를 연결하십시오.

각각의 이점, 비용 및 결과는 무엇입니까? 다른 대안이 있습니까?

이 토론이 정기적으로 이루어 지지만 스택 오버플로에 대해서는 아직 논의되지 않았다고 생각합니다 (일부 관련된 토론이 있음). 좋은 Google 검색 결과가 놀랍게도 부족합니다.



답변

어느 것이 가장 좋은 것이 아니라 무엇을 사용해야할지는 중요합니다.

‘정상적인’경우에는 간단한 질문만으로 상속 또는 집계가 필요한지 알 수 있습니다.

  • 새 클래스 원래 클래스와 다소 차이가있는 경우 상속을 사용하십시오. 새 클래스는 이제 원래 클래스의 서브 클래스입니다.
  • 새 클래스 에 원래 클래스 있어야합니다 . 집계를 사용하십시오. 새 클래스에는 이제 원래 클래스가 멤버로 있습니다.

그러나 큰 회색 영역이 있습니다. 따라서 몇 가지 다른 트릭이 필요합니다.

  • 상속을 사용했거나 인터페이스를 사용할 계획이지만 인터페이스의 일부만 사용하거나 상관 관계를 논리적으로 유지하기 위해 많은 기능을 재정의해야합니다. 그런 다음 집계를 사용해야 함을 나타내는 큰 불쾌한 냄새가납니다.
  • 집계를 사용했거나 사용하려는 경우 거의 모든 기능을 복사해야합니다. 그런 다음 상속의 방향을 가리키는 냄새가납니다.

짧게 자르려면 비논리적 상황을 피하기 위해 인터페이스의 일부를 사용하지 않거나 변경해야하는 경우 집계를 사용해야합니다. 주요 변경없이 거의 모든 기능이 필요한 경우 상속 만 사용하면됩니다. 의심스러운 경우 집계를 사용하십시오.

원래 클래스의 기능 중 일부가 필요한 클래스가있는 경우 다른 가능성은 원래 클래스를 루트 클래스와 하위 클래스로 분할하는 것입니다. 그리고 새로운 클래스가 루트 클래스에서 상속되도록합니다. 그러나 비논리적 인 분리를 만들지 말고 조심해야합니다.

예제를 추가하자. ‘Eat’, ‘Walk’, ‘Bark’, ‘Play’와 같은 메소드가있는 ‘Dog’클래스가 있습니다.

class Dog
  Eat;
  Walk;
  Bark;
  Play;
end;

이제 ‘Cat’클래스가 필요합니다.이 클래스에는 ‘Eat’, ‘Walk’, ‘Purr’및 ‘Play’가 필요합니다. 먼저 먼저 Dog에서 확장 해보십시오.

class Cat is Dog
  Purr;
end;

알았어.하지만 기다려. 이 고양이는 짖을 수 있습니다. 그리고 짖는 고양이는 우주의 원리를 위반합니다. 따라서 Bark 메서드를 재정 의하여 아무것도하지 않도록해야합니다.

class Cat is Dog
  Purr;
  Bark = null;
end;

좋아, 이것은 효과가 있지만 나쁜 냄새가 난다. 따라서 집계를 시도하십시오.

class Cat
  has Dog;
  Eat = Dog.Eat;
  Walk = Dog.Walk;
  Play = Dog.Play;
  Purr;
end;

좋아, 이거 좋다 이 고양이는 더 이상 짖지 않으며 심지어 침묵하지 않습니다. 그러나 여전히 내부 개가 필요합니다. 솔루션 3 번을 시도해보십시오.

class Pet
  Eat;
  Walk;
  Play;
end;

class Dog is Pet
  Bark;
end;

class Cat is Pet
  Purr;
end;

이것은 훨씬 더 깨끗합니다. 내부 개가 없습니다. 고양이와 개는 같은 수준입니다. 모델을 확장하기 위해 다른 애완 동물을 소개 할 수도 있습니다. 물고기 나 걷지 않는 것이 아니라면. 이 경우 다시 리팩토링해야합니다. 그러나 그것은 다른 시간을위한 것입니다.


답변

의 시작 부분에서 GOF 그들은 상태

클래스 상속보다 객체 구성을 선호합니다.

이것은 여기서 더 논의 됩니다


답변

차이는 일반적으로 “is a”와 “has a”의 차이로 표현됩니다. “is a”관계인 상속은 Liskov 대체 원칙에 잘 요약되어 있습니다. “a”관계인 집계는 그저 집계 개체에 집계 된 개체 중 하나 있음을 나타냅니다 .

C ++의 개인 상속은 “비록 노출 된”멤버 객체의 집계로도 모델링 될 수있는 “의 측면에서 구현 됨”관계를 나타냅니다.


답변

가장 일반적인 주장은 다음과 같습니다.

모든 객체 지향 시스템에서 모든 클래스에는 두 부분이 있습니다.

  1. 인터페이스 : 개체의 “공공의 얼굴”. 이것은 다른 세계에 발표하는 일련의 기능입니다. 많은 언어에서이 세트는 “클래스”로 잘 정의되어 있습니다. 일반적으로 이것들은 객체의 메소드 서명이지만 언어마다 조금씩 다릅니다.

  2. 그것의 구현 : 객체가 인터페이스를 만족시키고 기능을 제공하기 위해 수행하는 “무대 뒤에서”일. 이것은 일반적으로 객체의 코드 및 멤버 데이터입니다.

OOP의 기본 원칙 중 하나는 구현이 클래스 내에 캡슐화되어 있다는 것입니다 . 외부인이 볼 수있는 유일한 것은 인터페이스입니다.

서브 클래스는 서브 클래스에서 상속 될 때 일반적으로 구현과 인터페이스를 모두 상속 합니다 . 이것은 결국 클래스에 대한 제약으로 두 가지를 모두 받아 들여야 한다는 것을 의미합니다 .

집계를 사용하면 구현 또는 인터페이스 중 하나 또는 둘 다를 선택할 수 있지만 어느 쪽도 강요하지는 않습니다. 객체의 기능은 객체 자체에 달려 있습니다. 다른 객체를 원하는대로 연기 할 수 있지만 궁극적으로 자체 책임이 있습니다. 내 경험상 이것은 더 유연한 시스템, 즉 수정하기 쉬운 시스템으로 이어집니다.

따라서 객체 지향 소프트웨어를 개발할 때마다 거의 항상 상속보다 집계를 선호합니다.


답변

나는 “Is a”vs “Has a”에 대한 대답을했습니다 : 어느 것이 더 낫습니까? .

기본적으로 나는 다른 사람들과 동의합니다. 파생 클래스 동일한 데이터를 포함 하기 때문에 아니라 확장하는 유형 인 경우에만 상속을 사용하십시오 . 상속은 서브 클래스가 데이터뿐만 아니라 메소드도 얻는다는 것을 기억하십시오.

파생 클래스가 수퍼 클래스의 모든 메서드를 갖는 것이 합리적입니까? 아니면 파생 클래스에서 해당 메소드를 무시해야한다고 조용히 약속합니까? 아니면 수퍼 클래스에서 메서드를 재정 의하여 자신을 실수로 만들지 않으므로 아무도 실수로 호출하지 않습니까? 또는 API 문서 생성 도구에 힌트를 제공하여 문서에서 메소드를 생략 하시겠습니까?

이러한 경우 집계가 더 나은 선택이라는 강력한 단서입니다.


답변

이 질문과 관련 질문에 대해 “is-a vs. has-a; 개념적으로 다르다”는 답변이 많이 있습니다.

내 경험에서 찾은 한 가지는 관계가 “is-a”인지 “has-a”인지 확인하려고하면 실패한다는 것입니다. 지금 객체를 올바르게 결정할 수 있다고해도 요구 사항을 변경하면 나중에 언젠가 잘못되었을 수 있습니다.

내가 찾은 또 다른 것은 상속 계층 구조 주위에 많은 코드가 작성되면 상속에서 집계로 변환 하기가 매우 어렵다는 것입니다. 수퍼 클래스에서 인터페이스로 전환하면 시스템의 거의 모든 서브 클래스가 변경됩니다.

그리고이 포스트의 다른 곳에서 언급했듯이 집계는 상속보다 유연성이 떨어지는 경향이 있습니다.

따라서 하나 또는 다른 것을 선택해야 할 때마다 상속에 대한 완벽한 논쟁이 일어납니다.

  1. 당신의 선택은 어느 시점에서 잘못된 것일 수 있습니다
  2. 일단 선택을 변경하면 어렵습니다.
  3. 상속은 더 제한적이기 때문에 더 나쁜 선택 인 경향이 있습니다.

따라서 강력한 is-a 관계가있는 경우에도 집계를 선택하는 경향이 있습니다.


답변

이 질문은 일반적으로 Composition vs. Inheritance 로 표시되며 여기에서 이전에 요청되었습니다.