다중 상속 사용 사례


15

Java는 언어를 단순하게 유지하려는 디자인 목표를 없애기 위해 여러 상속을 생략 합니다 .

Java (에코 시스템 포함)가 실제로 "단순"한지 궁금합니다. 파이썬은 복잡하지 않으며 여러 상속이 있습니다. 그래서 너무 주관적이지 않으면 서 제 질문은 ...

다중 상속을 많이 사용하도록 설계된 코드에서 이점을 얻는 일반적인 문제 패턴은 무엇입니까?


8
Java는 아주 작은 이유로 완벽하게 좋은 것들을 많이 생략합니다. 나는 MI에 대한 적절한 정당화를 기대하지 않을 것이다.
DeadMG

2
파이썬의 다중 상속은 분명히 용의 영토입니다. 깊이 우선, 왼쪽에서 오른쪽 이름 확인 사용한다는 사실은 유지 관리 및 이해에 중요한 문제가 있습니다. 얕은 계층 구조에서는 유용 할 수 있지만, 심층 계층에서는 매우 반 직관적 일 수 있습니다.
Mark Booth

Java에 다중 상속이 포함되지 않은 이유는 Java 개발자가 자신의 언어를 배우기 쉽기를 원했기 때문이라고 생각합니다. 경우에 따라 엄청나게 강력하지만 다중 상속은 파악하기가 어렵고 좋은 효과를내는 데 더 어려워집니다. 프로그래밍 신입생과 대면하고 싶은 것은 아닙니다. 내 말은 : 상속 개념 자체에 어려움을 겪고있는 사람에게 가상 상속을 어떻게 설명합니까? 그리고 다중 상속도 구현 자 측에서 정확히 사소한 것이 아니기 때문에 Java 개발자는 그것을 생략하는 것이 윈윈입니다.
cmaster-복원 monica

Java는 명목상 타이핑됩니다. 파이썬은 그렇지 않습니다. 이를 통해 파이썬에서 여러 상속을 훨씬 쉽게 구현하고 이해할 수 있습니다.
Jules

답변:


11

찬성 :

  1. 때로는 다른 방법으로 문제를 모델링하는 것보다 명백한 문제 모델링이 가능합니다.
  2. 다른 패 런트가 직교 목적을 가지고 있다면 어떤 종류의 합성을 허용 할 수 있습니다

단점 :

  1. 다른 부모가 직교 목적을 가지고 있지 않으면 유형을 이해하기 어렵습니다.
  2. 언어 (모든 언어)로 어떻게 구현되는지 이해하기 쉽지 않습니다.

C ++에서 직교 피처를 합성하는 데 사용되는 다중 상속의 좋은 예는 CRTP 를 사용 하여 예를 들어 게임의 구성 요소 시스템을 설정하는 경우입니다.

예제를 작성하기 시작했지만 실제 예제가 더 가치가 있다고 생각합니다. Ogre3D의 일부 코드는 훌륭하고 직관적 인 방식으로 다중 상속을 사용합니다. 예를 들어, Mesh 클래스는 Resources와 AnimationContainer에서 상속받습니다. 리소스는 모든 리소스에 공통 인 인터페이스를 노출하고 AnimationContainer는 애니메이션 세트를 조작하기위한 인터페이스를 노출합니다. 그것들은 관련이 없으므로 메쉬를 애니메이션 세트를 포함 할 수있는 리소스로 생각하기 쉽습니다. 자연스럽지 않습니까?

이 라이브러리 에서 다른 예제를 살펴볼 수 있습니다 . 클래스가 새로운 CRTP 클래스 오버로드를 새로 작성하여 삭제 함으로써 메모리 할당이 세분화 된 방식으로 관리되는 방식 과 같습니다 .

앞서 언급했듯이 다중 상속의 주요 문제는 관련 개념을 혼합하여 발생합니다. 언어가 복잡한 구현을 설정해야하고 (C ++이 다이아몬드 문제를 해결할 수있는 방법을 참조하십시오 ...) 사용자는 해당 구현에서 무슨 일이 일어나고 있는지 확신하지 못합니다. 예를 들어, C ++에서 구현되는 방법을 설명하는이 기사를 읽으십시오 .

언어에서 언어를 제거하면 언어가 어떻게 나쁜지를 강요하는지 모르는 사람들을 피할 수 있습니다. 그러나 때로는 자연스럽게 느껴지지 않는 방식으로 생각해야합니다. 심지어 경우에도 당신이 생각할 수있는 더 자주 발생합니다.


예를 들어 "직교 목적"과 같은 용어를 더 명확하게 만들 것입니다-하지만 감사합니다
treecoder

좋아, 뭔가 추가하려고합니다.
클라 임

Ogre3D는 디자인 영감을 얻을 수있는 곳이 아닙니다. 싱글 톤 감염을 보셨습니까?
DeadMG

첫째, 상속인 싱글 톤은 실제로 싱글 톤이 아니며, 구조와 파괴는 명백합니다. 다음으로, Ogre는 하드웨어 시스템 (또는 원하는 경우 그래픽 드라이버)의 계층입니다. 즉, 시스템 인터페이스 (루트 또는 기타와 같은)에 대해 하나의 고유 한 표현 만 있어야합니다. 그들은 일부 싱글 톤을 제거 할 수 있지만 여기서 중요한 것은 아닙니다. 나는 트롤 토론을 피하기 위해 자발적으로 이것을 피했다. 그래서, 내가 지적한 예를보십시오. Singleton을 사용하는 것이 완벽하지는 않지만 실제로는 유용합니다 (그러나 모든 종류의 시스템에만 해당).
클라 임

4

보다 역동적 인 언어로 많이 사용되는 믹스 인 이라는 개념 이 있습니다. 다중 상속은 언어가 믹스 인을 지원할 수있는 방법 중 하나입니다. 믹스 인은 일반적으로 클래스가 다양한 기능을 축적하는 방법으로 사용됩니다. 다중 상속이 없으면 집계 / 위임을 사용하여 클래스와의 믹스 인 유형 동작을 가져와야합니다.


+1 이것은 실제로 여러 상속을 갖는 좋은 이유입니다. 믹스 인은 추가적인 의미를 가지고 있습니다 ( "이 클래스는 독립형으로 사용되어서는 안됩니다")
ashes999

2

선택은 주로 다이아몬드 문제 로 인한 문제에 근거한다고 생각합니다 .

또한 위임이나 다른 방법으로 다중 상속 사용을 회피하는 것이 종종 가능합니다.

마지막 질문의 의미를 잘 모르겠습니다. 그러나 "어떤 경우에 다중 상속이 유용합니까?"인 경우 기본적으로 객체 B와 C의 기능을 갖는 객체 A를 갖고 싶은 모든 경우에 적용됩니다.


2

나는 여기서 많이 탐구하지는 않지만 다음 링크 http://docs.python.org/release/1.5.1p1/tut/multiple.html을 통해 파이썬의 다중 상속을 확실히 이해할 수 있습니다 .

의미를 설명하는 데 필요한 유일한 규칙은 클래스 속성 참조에 사용되는 해결 규칙입니다. 이것은 깊이 우선, 왼쪽에서 오른쪽입니다. 따라서, DerivedClassName에서 속성을 찾을 수없는 경우 Base1에서 속성을 검색 한 다음 Base1의 기본 클래스에서 (반복적으로) 속성을 찾을 수없는 경우에만 Base2에서 검색합니다.

...

우연한 이름 충돌을 피하기위한 규칙에 대한 파이썬의 의존을 고려할 때 다중 상속의 무차별 사용은 유지 보수의 악몽이라는 것이 분명합니다. 다중 상속에서 잘 알려진 문제는 공통 기본 클래스를 갖는 두 클래스에서 파생 된 클래스입니다. 이 경우에 발생하는 상황 (인스턴스 변수의 단일 사본 또는 공통 기본 클래스에서 사용하는 데이터 속성이 있음)을 파악하기는 쉽지만 이러한 의미가 어떤 식 으로든 확실하지 않습니다. 유능한.

이것은 단지 작은 단락이지만 추측 한 의심을 없애기에 충분합니다.


1

다중 상속이 유용한 한 곳은 클래스가 여러 인터페이스를 구현하는 상황이지만 각 인터페이스에 기본 기능이 내장되어 있어야합니다. 이것은 일부 인터페이스를 구현하는 대부분의 클래스가 동일한 방식으로 무언가를 원하지만 때로는 다른 것을해야하는 경우에 유용합니다. 각 클래스를 동일한 구현으로 가질 수 있지만 한 위치로 밀어 넣는 것이 더 합리적입니다.


1
일반화 된 다중 상속 또는 단순히 인터페이스가 구현되지 않은 메소드에 대한 기본 동작을 지정할 수있는 수단이 필요한가? 만약 인터페이스가 다른 인터페이스로부터 상속받은 것과는 달리 그들이 구현 한 메소드에 대한 기본 구현만을 지정할 수 있다면, 그러한 기능은 다중 상속을 어렵게 만드는 이중 다이아몬드 문제를 완전히 피할 것입니다.
supercat

1

다중 상속을 많이 사용하도록 설계된 코드에서 이점을 얻는 일반적인 문제 패턴은 무엇입니까?

이것은 단지 하나의 예일뿐입니다. 그러나 안전을 개선하고 호출자 또는 하위 클래스 전체에 걸쳐 계단식 변경 사항을 적용하려는 유혹을 완화하는 데 귀중한 것으로 나타났습니다.

가장 추상적 인 경우에도 다중 상속이 매우 유용하다는 것을 알았을 때 상태 비 저장 인터페이스는 C ++의 비가 상 인터페이스 관용구 (NVI)입니다.

그들은 계약의 보편적 측면을 강제하기 위해 약간의 구현을 가진 인터페이스 와 같이 기본 클래스를 실제로 추상화 하지도 않습니다. .

간단한 예 (일부 전달 된 파일 핸들이 열려 있는지 또는 이와 비슷한 지 확인할 수 있음) :

// Non-virtual interface (public methods are nonvirtual/final).
// Since these are modeling the concept of "interface", not ABC,
// multiple will often be inherited ("implemented") by a subclass.
class SomeInterface
{
public:
    // Pre: x should always be greater than or equal to zero.
    void f(int x) /*final*/
    {
        // Make sure x is actually greater than or equal to zero
        // to meet the necessary pre-conditions of this function.
        assert(x >= 0);

        // Call the overridden function in the subtype.
        f_impl(x);
    }

protected:
    // Overridden by a boatload of subtypes which implement
    // this non-virtual interface.
    virtual void f_impl(int x) = 0;
};

이 경우 f코드베이스의 천 곳에서 호출되고 f_impl백 개의 하위 클래스로 대체됩니다.

이런 종류의 안전 점검은 전화를 거는 1000 곳 f또는 재정의하는 100 곳 모두 에서 수행하기 어려울 것 f_impl입니다.

이 진입 점을 기능을 비 가상화로 지정하면이 검사를 수행 할 수있는 중심 위치가됩니다. 그리고이 검사는 추상화를 최소화하지 않습니다. 단순히이 함수를 호출하는 데 필요한 전제 조건을 주장하기 때문입니다. 어떤 의미에서, 그것은 인터페이스가 제공하는 계약을 강화하고, x입력을 점검하는 것이 100 개의 모든 장소에서 유효한 전제 조건을 준수하는지 확인하기위한 부담을 덜어줍니다 .

모든 언어가 C ++ 에서조차도 기본 개념에 가깝기를 원했습니다 (예 : 재정의하기 위해 별도의 함수를 정의하지 않아도 됨).

이것은 assert사전 에이 작업을 수행하지 않은 경우 매우 유용 하며 나중에 코드베이스의 임의의 임의의 위치에 음수 값이 전달 될 때 필요하다는 것을 깨달았습니다 f.


0

첫 번째 : 기본 클래스의 여러 사본 (C ++ 문제) 및 기본 클래스와 파생 클래스 간의 긴밀한 연결.

둘째 : 추상 인터페이스에서 다중 상속


어떤 상황에서도 유용하지 않다고 제안하고 있습니까? 그리고 모든 것이 편리하게 설계 / 코딩 될 수 있습니까? 또한 두 번째 요점을 자세히 설명하십시오.
treecoder
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.