web-dev-qa-db-ja.com

PHPの多重継承

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

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

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

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

ありがとう!

97
Alex Weinstein

アレックス、ほとんどの場合、多重継承が必要なのは、オブジェクト構造がやや不正確なシグナルです。あなたが概説した状況では、クラスの責任は単純に広すぎます。 Messageがアプリケーションのビジネスモデルの一部である場合、出力のレンダリングは考慮すべきではありません。代わりに、責任を分割し、テキストまたはhtmlバックエンドを使用して渡されたメッセージを送信するMessageDispatcherを使用できます。あなたのコードはわかりませんが、この方法でシミュレートしてみましょう。

$m = new Message();
$m->type = 'text/html';
$m->from = 'John Doe <[email protected]>';
$m->to = 'Random Hacker <[email protected]>';
$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は、渡されたMessageオブジェクトのtypeプロパティに応じて、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クラスで行われ、送信アルゴリズムはディスパッチャーに委任されます。これは戦略パターンと呼ばれ、詳細についてはこちらをご覧ください こちら

142

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

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

14

このスレッド ...でPhilを引用できる場合.

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

入ってくるPHP 5.4は traits この問題の解決策を提供しようとする。

それまでの間、クラスの設計を再考するのが最善でしょう。クラスの拡張APIを使用している場合は、複数のインターフェイスを実装できます。

そしてクリス...

PHPは複数の継承を実際にはサポートしていませんが、いくつかの(やや厄介な)実装方法があります。いくつかの例については、このURLをご覧ください。

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

両方とも便利なリンクがあると思った。特性や多分いくつかのミックスインを試してみるのを待つことができません...

9
Simon East

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

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

6
joelhardi

これを解決する方法としてPHP 5.4で特性を使用しています。 http://php.net/manual/en/language.oop5.traits.php

これにより、拡張による古典的な継承が可能になりますが、共通の機能とプロパティを「特性」に配置することもできます。マニュアルが言うように:

Traitsは、PHPなどの単一継承言語でコードを再利用するためのメカニズムです。 Traitは、開発者が異なるクラス階層に存在するいくつかの独立したクラスでメソッドのセットを自由に再利用できるようにすることで、単一継承の制限を軽減することを目的としています。

4
MatthewPearson

これは質問でもあり解決策でもあります。

魔法の_call()、 _ get()、__ set()メソッドはどうですか?このソリューションはまだテストしていませんが、multiInheritクラスを作成するとどうなりますか。子クラスの保護された変数には、継承するクラスの配列を含めることができます。マルチインターフェイスクラスのコンストラクターは、継承されている各クラスのインスタンスを作成し、_extなどのプライベートプロパティにリンクできます。 __call()メソッドは、_ext配列内の各クラスでmethod_exists()関数を使用して、呼び出す正しいメソッドを見つけることができます。 __get()および__setを使用して内部プロパティを見つけることができます。または、参照を持つエキスパートが子クラスと継承されたクラスのプロパティを同じデータへの参照にすることができます。オブジェクトの多重継承は、それらのオブジェクトを使用するコードに対して透過的です。また、_ext配列がクラス名でインデックス付けされている限り、内部オブジェクトは必要に応じて継承されたオブジェクトに直接アクセスできます。私はこのスーパークラスを作成することを想定しており、それが機能する場合、さまざまな悪いプログラミング習慣の開発につながる可能性があると感じているため、まだ実装していません。

3
Ralph Ritoch

decorator pattern が適切かもしれませんが、詳細なしではわかりにくいようです。

3
danio

あなたが何をしているのかを明確にするためにいくつか質問があります。

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

あまりにも多くの機能を、メッセージの内容の保持にのみ関係するオブジェクトのセットに組み合わせようとしているように聞こえますが、メッセージの処理方法ではありません。ご存知のように、招待状と標準メッセージの間に違いはありません。招待に特別な処理が必要な場合は、メッセージタイプではなくアプリケーションロジックを意味します。

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

1
Dave

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

0
DeeCee

PHPはインターフェイスをサポートします。ユースケースにもよりますが、これは良い方法です。

0
Cheekysoft