PHP의 다중 상속


97

PHP5가 여전히 다중 상속을 지원하지 않는다는 사실을 우회 할 수있는 좋고 깨끗한 방법을 찾고 있습니다. 다음은 클래스 계층 구조입니다.

메시지
-TextMessage
-------- InvitationTextMessage
-EmailMessage
-------- InvitationEmailMessage

두 가지 유형의 Invitation * 수업은 공통점이 많습니다. 둘 다 상속받을 공통 부모 클래스 인 Invitation을 갖고 싶습니다. 안타깝게도 현재 조상 인 TextMessage 및 EmailMessage와 공통점이 많습니다. 여기에서 다중 상속에 대한 고전적인 욕구.

문제를 해결하기위한 가장 가벼운 접근 방식은 무엇입니까?

감사!


4
상속 (또는 다중 상속)이 정당한 경우는 많지 않습니다. SOLID 원칙을 살펴보십시오. 상속보다 구성을 선호합니다.
Ondřej Mirtes

2
@ OndřejMirtes 무슨 뜻입니까- "상속이 정당한 경우가 많지 않습니다."?
styler1972

12
내 말은-상속은 혜택보다 더 많은 문제를 가져옵니다 (Liskov 대체 원칙을보세요). 구성으로 거의 모든 것을 해결할 수 있으며 많은 두통을 줄일 수 있습니다. 상속도 정적입니다. 즉, 이미 코드에 작성된 내용을 변경할 수 없습니다. 그러나 합성은 런타임에 사용할 수 있으며 구현을 동적으로 선택할 수 있습니다. 예를 들어 다른 캐싱 메커니즘으로 동일한 클래스를 재사용 할 수 있습니다.
Ondřej Mirtes

5
: PHP 5.4 "특성"을 가지고 stackoverflow.com/a/13966131/492130
f.ardelian

1
나는 초보자가 상속을 사용하지 않는 것이 좋습니다 . 일반적으로 상속이 허용되는 유일한 두 가지 상황은 다음과 같습니다 : 1) 사용자가 적은 코드 2) 프로젝트 리드가 요구 될 때 당신이 그것을 사용을 쓰기 때문에, 라이브러리를 구축 할 때
gurghet

답변:


141

Alex, 다중 상속이 필요한 대부분의 경우 객체 구조가 다소 올바르지 않다는 신호입니다. 당신이 설명한 상황에서 나는 당신이 단순히 너무 광범위한 계급 책임을 가지고 있음을 알았습니다. 메시지가 애플리케이션 비즈니스 모델의 일부인 경우 출력 렌더링에 신경 쓰지 않아야합니다. 대신 책임을 분할하고 텍스트 또는 html 백엔드를 사용하여 전달 된 메시지를 보내는 MessageDispatcher를 사용할 수 있습니다. 귀하의 코드를 모르지만 다음과 같이 시뮬레이션 해 보겠습니다.

$m = new Message();
$m->type = 'text/html';
$m->from = 'John Doe <jdoe@yahoo.com>';
$m->to = 'Random Hacker <rh@gmail.com>';
$m->subject = 'Invitation email';
$m->importBody('invitation.html');

$d = new MessageDispatcher();
$d->dispatch($m);

이렇게하면 Message 클래스에 몇 가지 전문화를 추가 할 수 있습니다.

$htmlIM = new InvitationHTMLMessage(); // html type, subject and body configuration in constructor
$textIM = new InvitationTextMessage(); // text type, subject and body configuration in constructor

$d = new MessageDispatcher();
$d->dispatch($htmlIM);
$d->dispatch($textIM);

MessageDispatcher는 type전달 된 Message 객체의 속성에 따라 HTML 또는 일반 텍스트로 보낼지 여부를 결정합니다 .

// in MessageDispatcher class
public function dispatch(Message $m) {
    if ($m->type == 'text/plain') {
        $this->sendAsText($m);
    } elseif ($m->type == 'text/html') {
        $this->sendAsHTML($m);
    } else {
        throw new Exception("MIME type {$m->type} not supported");
    }
}

요약하자면 책임은 두 클래스로 나뉩니다. 메시지 구성은 InvitationHTMLMessage / InvitationTextMessage 클래스에서 이루어지며 발송 알고리즘은 발송자에게 위임됩니다. 이를 전략 패턴이라고하며 여기에서 자세한 내용을 읽을 수 있습니다 .


13
놀랍도록 광범위한 답변, 감사합니다! 오늘 배운 게 있어요!
Alex Weinstein

26
... 나는 이것이 조금 오래되었다는 것을 알고 있습니다 (PHP에 MI가 있는지 알아보기 위해 ... 호기심을 위해) 나는 이것이 전략 패턴의 좋은 예라고 생각하지 않습니다. 전략 패턴은 언제든지 새로운 "전략"을 구현할 수 있도록 설계되었습니다. 제공 한 구현에는 이러한 기능이 없습니다. 대신 Message는 MessageDispatcher-> dispatch ()를 호출하는 "send"함수를 가져야합니다 (Dispatcher는 param 또는 멤버 var). 새로운 클래스 HTMLDispatcher & TextDispatcher는 각각의 방식으로 "dispatch"를 구현합니다 (이렇게하면 다른 Dispatcher가 다른 작품)
Terence Honles 2010 년

12
불행히도 PHP는 전략 패턴 구현에 적합하지 않습니다. 메서드 오버로딩을 지원하는 언어가 여기에서 더 잘 작동합니다. 같은 이름의 두 메서드 인 dispatch (HTMLMessage $ m) 및 dispatch (TextMessage $)가 있다고 가정합니다. 이제 강력한 형식의 언어 컴파일러 / 인터프리터는 다음을 기반으로 올바른 "전략"을 자동으로 사용합니다. 매개 변수 유형. 그 외에도 새로운 전략 구현을위한 개방이 전략 패턴의 본질이라고 생각하지 않습니다. 물론 가지고 있으면 좋지만 종종 요구 사항은 아닙니다.
Michał Rudnicki

2
Tracing파일로 디버그하고 중요한 문제에 대해 SMS를 보내는 것과 같은 일반적인 작업을 원하는 클래스 (이것은 샘플 일뿐입니다)가 있다고 가정합니다 . 모든 수업은이 수업의 하위입니다. 이제 Exception이러한 함수 (= 자식의 Tracing) 가 있어야 하는 클래스를 만들고 싶다고 가정합니다 . 이 클래스는의 자식이어야합니다 Exception. 다중 상속 없이 그러한 것들을 어떻게 디자인 합니까? 예, 항상 해결책이있을 수 있지만 항상 해킹에 가까워 질 것입니다. 그리고 해킹 = 장기적으로 비용이 많이 드는 솔루션. 이야기의 끝.
Olivier Pons

1
Olivier Pons, Tracing을 서브 클래 싱하는 것이 귀하의 사용 사례에 적합한 솔루션이라고 생각하지 않습니다. 정적 메서드 Debug, SendSMS 등이있는 추상 Tracing 클래스를 갖는 것처럼 간단한 것입니다. 그러면 Tracing :: SendSMS () 등을 사용하여 다른 클래스 내에서 호출 할 수 있습니다. 다른 클래스는 Tracing의 '유형'이 아닙니다. 그들은 추적을 '사용'합니다. 참고 : 어떤 사람들은 정적 메서드보다 싱글 톤을 선호 할 수 있습니다. 가능한 경우 싱글 톤보다 정적 메서드를 사용하는 것을 선호합니다.

15

'is-a'관계를 'has-a'관계로 바꿀 수 있습니까? 초대에는 메시지가있을 수 있지만 반드시 'is-a'메시지가 필요한 것은 아닙니다. 메시지 모델과 잘 어울리지 않는 Invitation fe가 확인 될 수 있습니다.

그것에 대해 더 알고 싶다면 '구성 vs. 상속'을 검색하십시오.


9

이 스레드 에서 필을 인용 할 수 있다면 ...

Java와 마찬가지로 PHP는 다중 상속을 지원하지 않습니다.

PHP 5.4에서 오는 것은 이 문제에 대한 해결책을 제공하려는 특성 이 될 것 입니다.

그 동안 수업 디자인을 다시 생각하는 것이 가장 좋습니다. 클래스에 대한 확장 API를 사용하는 경우 여러 인터페이스를 구현할 수 있습니다.

그리고 크리스 ....

PHP는 실제로 다중 상속을 지원하지 않지만이를 구현하는 몇 가지 (다소 지저분한) 방법이 있습니다. 다음 URL에서 몇 가지 예를 확인하세요.

http://www.jasny.net/articles/how-i-php-multiple-inheritance/

둘 다 유용한 링크가 있다고 생각했습니다. 특성이나 믹스 인을 시험 해보고 싶어서 기다릴 수 없습니다 ...


1
특성은가는 길이다
Jonathan

6

Symfony 프레임 워크에는이를위한 믹스 인 플러그인이 있습니다. 있습니다. 사용하지 않더라도 아이디어를 위해서라도 확인해보세요.

"디자인 패턴"에 대한 대답은 공유 기능을 별도의 구성 요소로 추상화하고 런타임에 구성하는 것입니다. 초대 기능을 상속 이외의 방식으로 Message 클래스와 연결되는 클래스로 추상화하는 방법을 생각해보십시오.


4

이 문제를 해결하는 방법으로 PHP 5.4의 특성을 사용하고 있습니다. http://php.net/manual/en/language.oop5.traits.php

이를 통해 확장을 통한 고전적인 상속이 가능하지만 공통 기능과 속성을 '특성'에 배치 할 수도 있습니다. 매뉴얼에 따르면 :

특성은 PHP와 같은 단일 상속 언어에서 코드를 재사용하기위한 메커니즘입니다. 트레이 트는 개발자가 다른 클래스 계층 구조에있는 여러 독립 클래스에서 메서드 세트를 자유롭게 재사용 할 수 있도록하여 단일 상속의 일부 제한을 줄이기위한 것입니다.



3

이것은 질문이자 해결책입니다 ....

마법 _은 어떻습니까? call ()_get (), __set () 메서드? 아직이 솔루션을 테스트하지는 않았지만 multiInherit 클래스를 만들면 어떨까요? 자식 클래스의 보호 된 변수에는 상속 할 클래스 배열이 포함될 수 있습니다. 다중 인터페이스 클래스의 생성자는 상속되는 각 클래스의 인스턴스를 만들고 _ext와 같은 전용 속성에 연결할 수 있습니다. __call () 메서드는 _ext 배열의 각 클래스에서 method_exists () 함수를 사용하여 호출 할 올바른 메서드를 찾을 수 있습니다. __get () 및 __set을 사용하여 내부 속성을 찾거나 참조가있는 전문가 인 경우 자식 클래스의 속성과 상속 된 클래스를 동일한 데이터에 대한 참조로 만들 수 있습니다. 객체의 다중 상속은 해당 객체를 사용하는 코드에 투명합니다. 또한, 내부 객체는 _ext 배열이 클래스 이름으로 인덱싱되는 한 필요한 경우 상속 된 객체에 직접 액세스 할 수 있습니다. 나는이 슈퍼 클래스를 만들 계획을 세웠고, 그것이 작동한다면 여러 가지 나쁜 프로그래밍 습관을 개발할 수 있다고 생각하므로 아직 구현하지 않았습니다.


나는 이것이 가능하다고 생각합니다. 여러 클래스의 기능을 결합하지만 실제로 상속하지는 않습니다 (의 의미에서 instanceof)
user102008

그리고 이것은 내부 클래스에서 self :: <whatever>를 호출하자마자 재정의를 허용하지 못할 것입니다.
Phil Lello

1

수행중인 작업을 명확히하기 위해 몇 가지 질문이 있습니다.

1) 메시지 개체에 본문, 수신자, 일정 시간과 같은 메시지 포함되어 있습니까? 2) Invitation 개체로 무엇을 하시겠습니까? EmailMessage와 비교하여 특별히 처리해야합니까? 3) 그렇다면 무엇이 그렇게 특별합니까? 4) 그렇다면 초대 메시지 유형이 다르게 처리되어야하는 이유는 무엇입니까? 5) 환영 메시지 나 OK 메시지를 보내려면 어떻게해야합니까? 그것들도 새로운 물건입니까?

메시지 내용을 처리하는 방법이 아니라 메시지 내용을 보유하는 데만 관심을 가져야하는 개체 집합에 너무 많은 기능을 결합하려는 것처럼 들립니다. 저에게는 초대 메시지와 표준 메시지간에 차이가 없습니다. 초대에 특별한 처리가 필요한 경우 메시지 유형이 아닌 응용 프로그램 논리를 의미합니다.

예 : 내가 구축 한 시스템에는 SMS, 이메일 및 기타 메시지 유형으로 확장 된 공유 기본 메시지 개체가 있습니다. 그러나 이것은 더 이상 확장되지 않았습니다. 초대 메시지는 단순히 이메일 유형의 메시지를 통해 전송되도록 미리 정의 된 텍스트였습니다. 특정 초대 응용 프로그램은 초대에 대한 유효성 검사 및 기타 요구 사항과 관련이 있습니다. 결국, 당신이 원하는 것은 메시지 X를 수신자 Y에게 보내는 것입니다. 이것은 그 자체로 별개의 시스템이어야합니다.


0

Java와 같은 문제입니다. 추상 함수가있는 인터페이스를 사용하여 문제를 해결해보십시오.


0

PHP는 인터페이스를 지원합니다. 사용 사례에 따라 좋은 방법이 될 수 있습니다.


5
인터페이스는 구체적인 기능 구현을 허용하지 않으므로 여기서는 도움이되지 않습니다.
Alex Weinstein

1
인터페이스는 클래스와 달리 다중 상속을 지원합니다.
Craig Lewis

-1

Message 클래스 바로 아래에있는 Invitation 클래스는 어떻습니까?

따라서 계층 구조는 다음과 같습니다.

메시지
--- 초대
------ TextMessage
------ EmailMessage

그리고 Invitation 클래스에서 InvitationTextMessage 및 InvitationEmailMessage에 있던 기능을 추가합니다.

초대가 실제로 메시지 유형이 아니라 메시지 기능에 가깝다는 것을 알고 있습니다. 그래서 이것이 좋은 OO 디자인인지 아닌지 잘 모르겠습니다.

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