PHPの多重継承


97

PHP5はまだ多重継承をサポートしていないという事実を回避するための良い、きれいな方法を探しています。これがクラス階層です:

メッセージ
-TextMessage
-------- InvitationTextMessage
-EmailMessage
-------- InvitationEmailMessage

2種類のInvitation *クラスには多くの共通点があります。両方が継承する共通の親クラスInvitationが欲しいです。残念ながら、彼らはまた、現在の祖先と多くの共通点を持っています... TextMessageおよびEmailMessage。ここで多重継承の古典的な欲求。

問題を解決するための最も軽量なアプローチは何ですか?

ありがとう!


4
継承(または多重継承)が正当化されるケースは多くありません。SOLIDの原則を見てください。継承よりも構成を優先します。
オンドレイMirtes

2
@OndřejMirtesどういう意味ですか-「継承が正当化されるケースは多くありません。」?
styler1972 2012年

12
つまり、継承はメリットよりも多くの問題をもたらします(リスコフの置換原理を見てください)。構図でほとんどすべてを解決でき、頭痛の種を大幅に節約できます。継承も静的です。つまり、すでにコードに記述されている内容を変更することはできません。しかし、合成は実行時に使用でき、実装を動的に選択できます。たとえば、異なるクラスのキャッシュメカニズムで同じクラスを再利用できます。
オンドレイMirtes

5
PHP 5.4には「特性」があります:stackoverflow.com/a/13966131/492130
f.ardelian

1
初心者は継承を使用ないことをお勧めします。一般に、継承が許可される状況は、1)ライブラリを作成してユーザーが書くコードが少ない場合と、2)プロジェクトリーダーが使用を要求した場合の2つの状況のみです
gurghet

回答:


141

アレックス、多重継承が必要な場合のほとんどは、オブジェクト構造が多少不正確であることを示す信号です。あなたが概説した状況では、私はあなたが単にクラスの責任が広すぎると思います。Messageがアプリケーションビジネスモデルの一部である場合、出力のレンダリングは考慮されません。代わりに、責任を分割し、渡されたメッセージをテキストまたは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");
    }
}

要約すると、責任は2つのクラスに分割されます。メッセージの構成はInvitationHTMLMessage / InvitationTextMessageクラスで行われ、送信アルゴリズムはディスパッチャーに委任されます。これは戦略パターンと呼ばれます。詳細については、こちらをご覧ください


13
驚異的な回答、ありがとうございます!今日は何かを学びました!
Alex Weinstein、

26
...私はこれが少し古いことを知っています(PHPにMIがあるかどうかを探していました...好奇心のために...)これは戦略パターンの良い例ではないと思います。戦略パターンは、いつでも新しい「戦略」を実装できるように設計されています。あなたが提供した実装は、そのような機能を備えていません。代わりに、MessageにはMessageDispatcher-> dispatch()(Dispatcherはparamまたはメンバーvarのいずれか)を呼び出す「send」関数が必要であり、新しいクラスHTMLDispatcher&TextDispatcherはそれぞれの方法で「ディスパッチ」を実装します(これにより、他のDispatcherが他の作品)
Terence Honles、2010年

12
残念ながら、PHPはStrategyパターンの実装には適していません。メソッドのオーバーロードをサポートする言語は、ここでより適切に機能します-同じ名前の2つのメソッド、dispatch(HTMLMessage $ m)とdispatch(TextMessage $)があるとします-厳密に型指定された言語コンパイラ/インタープリターでは、以下に基づいて適切な「戦略」を自動的に採用しますパラメータのタイプ。それを除けば、新しい戦略の実施のためにオープンであることは戦略パターンの本質であるとは思いません。確かにそれは良いことですが、多くの場合要件ではありません。
のMichałRudnicki

2
Tracingデバッグなどの一般的なものをファイルに入れたり、重大な問題のSMSを送信したりするクラス(これは単なるサンプル)があるとします。すべてのクラスはこのクラスの子です。次に、Exceptionこれらの関数(=の子Tracing)を持つクラスを作成するとします。このクラスはの子である必要がありますException。多重継承なしでそのようなものどのように設計しますか?はい、あなたは常に解決策を持っているかもしれませんが、あなたは常にハッキングに近づくでしょう。そして、ハッキング=長期的には高価なソリューションです。物語の終わり。
Olivier Pons 2014年

1
Olivier Ponsさん、トレーシングのサブクラス化がユースケースの正しい解決策になるとは思いません。静的メソッドDebug、SendSMSなどを含む抽象的なTracingクラスを作成するのと同じくらい簡単なもので、Tracing :: SendSMS()などを使用して他のクラス内から呼び出すことができます。他のクラスはTracingの「タイプ」ではありません。彼らはトレースを「使用」します。注意:静的メソッドよりもシングルトンを好む人もいます。私は可能な限りシングルトンよりも静的メソッドを使用することを好みます。

15

「is-a」関係を「has-a」関係に置き換えることができますか?招待にはメッセージが含まれる場合がありますが、必ずしも「is-a」メッセージである必要はありません。招待状が確認された可能性がありますが、これはメッセージモデルとうまく連動しません。

詳細については、「構成と継承」を検索してください。


9

このスレッドでフィルを引用できるとしたら...

PHPは、Javaと同様に、多重継承をサポートしていません。

PHP 5.4には、この問題の解決策を提供しようとする特性があります。

それまでの間は、クラスのデザインを再考することをお勧めします。クラスへの拡張APIが必要な場合は、複数のインターフェースを実装できます。

そしてクリス...

PHPは実際には多重継承をサポートしていませんが、それを実装するためのいくつかの(多少乱雑な)方法があります。いくつかの例については、このURLを確認してください。

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

彼らは両方とも有用なリンクを持っていたと思いました。トレイトやミックスインを試してみるのが待ちきれません...


1
トレイトは進むべき道
ジョナサン

6

Symfonyフレームワークには、このためのミックスインプラグインがあります。アイデアを使用する場合でも、使用しない場合は、チェックインすることをお勧めします。

「設計パターン」の答えは、共有機能を別のコンポーネントに抽象化し、実行時に構成することです。継承以外の方法でメッセージクラスに関連付けられるクラスとして、招待機能を抽象化する方法について考えます。


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

そして、これは確かにすぐに内部クラスのように自己への呼び出しを行うと上書きを許可するように失敗します:: <任意>
フィルLelloの

1

私はあなたが何をしているのかを明確にするために尋ねるべきいくつかの質問があります:

1)メッセージオブジェクトは、本文、受信者、スケジュール時刻などのメッセージのみが含まれていますか?2)招待オブジェクトをどうするつもりですか?EmailMessageと比較して特別に処理する必要がありますか?3)もしそうなら、それは何がそんなに特別なのですか?4)それがその場合である場合、なぜ招待のためにメッセージタイプを異なる方法で処理する必要があるのですか?5)ウェルカムメッセージまたはOKメッセージを送信する場合はどうなりますか?それらも新しいオブジェクトですか?

あまりにも多くの機能を、メッセージの内容の保持だけに関係するオブジェクトのセットに結合しようとしているように見えます。メッセージの処理方法ではありません。私にとっては、招待状と通常のメッセージの間に違いはありません。招待が特別な処理を必要とする場合、それはアプリケーションロジックを意味し、メッセージタイプを意味しません。

たとえば、私が構築したシステムには、SMS、電子メール、およびその他のメッセージタイプに拡張された共有ベースメッセージオブジェクトがありました。ただし、これらはさらに拡張されませんでした-招待メッセージは、事前に定義されたテキストであり、Eメールタイプのメッセージを介して送信されます。特定の招待アプリケーションは、招待の検証およびその他の要件に関係します。結局のところ、やりたいことは、メッセージXを受信者Yに送信することだけです。


0

Javaと同じ問題。その問題を解決するために抽象関数を備えたインターフェースを使用してみてください


0

PHPはインターフェースをサポートしています。ユースケースによっては、これは良い策になるかもしれません。


5
インターフェイスは具体的な関数の実装を許可しないため、ここでは役に立ちません。
Alex Weinstein、

1
クラスとは異なり、インターフェイスは多重継承をサポートします。
Craig Lewis、

-1

メッセージクラスのすぐ下にある招待クラスはどうですか?

したがって、階層は次のようになります。

メッセージ
---招待状
------ TextMessage
------ EmailMessage

そしてInvitationクラスに、InvitationTextMessageとInvitationEmailMessageにあった機能を追加します。

招待状は実際には一種のメッセージではなく、メッセージの機能です。したがって、これが適切なOOデザインであるかどうかはわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.