20

標準 PHP ライブラリには、一部のリソースが Observer パターンの参照実装SplSubjectと呼ぶものが含まれていSplObserverます。私の人生では、実際のイベントやその他の情報を通知とともに渡す方法がないため、これらがどのように非常に役立つかを理解できません。

class MySubject implements SplSubject {
    protected $_observers = [];

    public function attach(SplObserver $observer) {
        $id = spl_object_hash($observer);
        $this->_observers[$id] = $observer;
    }

    public function detach(SplObserver $observer) {
        $id = spl_object_hash($observer);

        if (isset($this->_observers[$id])) {
            unset($this->_observers[$id]);
        }
    }

    public function notify() {
        foreach ($this->_observers as $observer) {
            $observer->update($this);
        }
    }
}

class MyObserver implements SplObserver {
    public function update(SplSubject $subject) {
        // something happened with $subject, but what
        // was it???
    }
}

$subject = new MySubject();
$observer = new MyObserver();

$subject->attach($observer);
$subject->notify();

これらのインターフェースは、実際の問題にはほとんど役に立たないようです。誰かが私を啓発できますか?


編集:

インターフェイスに関する私の最大の問題は次のとおりです(他にもありますが):

public function update(SplSubject $subject, Event $event) { /* ... */ }

...次の致命的なエラーをネットします。

PHP Fatal error:  Declaration of MyObserver::update() must be compatible with SplObserver::update(SplSubject $SplSubject)

編集#2:

追加のパラメーターにデフォルトを指定してオプションにすることで、致命的なエラーを防ぎ、コンテキストを渡す方法を提供して、実装を価値あるものにします。私はこれまでこれに気づいていなかったので、これは私の質問にほとんど答えています。解決策は、独自のイベント/メッセージ データを渡し、 内にその存在を確認することSplObserver::update()です。

4

6 に答える 6

38

It seems like these interfaces are pretty much useless for any real world problem. Can someone enlighten me?

インターフェース

抽象クラスではある程度の実装を提供できますが、インターフェースは純粋なテンプレートです。缶interfaceのみ。define functionalityそれを実装することはできません。インターフェイスは、interface キーワードで宣言されます。プロパティとメソッド宣言を含めることができますが、メソッド本体を含めることはできません。

インターフェースのユースケース

たとえば、プロジェクトが異なるデータベースをサポートする必要がある場合。将来データベースを変更できるようにするには、オブジェクトを変更せずに、クラス ファイルにプロパティ プロシージャを含むインターフェイスを使用することをお勧めします

By itself, interfaces are not very usefulインターフェイスのインスタンスを作成することはできませんがwhich in real sense makes your live easier as a programmer、オブジェクト指向プログラミングの最も重要なインセンティブはカプセル化であるため、インターフェイスはオブジェクト指向の設計方法論を実施するのに役立ちます (機能がどのように実装されているかは気にしません。プログラマーは、これは、システム アーキテクチャを監視する良い方法でもあります)。

SplSubject & SplObserver

直交性は美徳です。プログラマーとしての目的の 1 つは、他のコンポーネントへの影響を最小限に抑えて変更または移動できるコンポーネントを構築することです。

1 つのコンポーネントに加えたすべての変更が、コードベースの別の場所での変更の波及を必要とする場合、開発タスクはすぐにバグの作成と削除のスパイラルになる可能性があります。

SplSubjectSplObserverは両方であるため、特別な機能はありません。interface to implement the Observer Design Pattern.

オブザーバーパターン

オブザーバー パターンは、サブジェクトと呼ばれるオブジェクトがオブザーバーと呼ばれるその従属オブジェクトのリストを維持し、通常はメソッドの 1 つを呼び出すことによって、状態の変化を自動的に通知するソフトウェア設計パターンです。主に分散イベント処理システムの実装に使用されます

  • オブザーバー パターンは、サブジェクト オブジェクトと任意の数のオブザーバー オブジェクトとの間の 1 対多の依存関係を定義するため、サブジェクト オブジェクトの状態が変化すると、そのすべてのオブザーバー オブジェクトに通知が送信され、自動的に更新されます。
  • オブザーバー パターンでは、基本的に、オブジェクト自体を登録することにより、無制限の数のオブジェクトが監視対象オブジェクト (またはサブジェクト) 内のイベントを監視またはリッスンできます。オブザーバーがイベントに登録された後、サブジェクトはイベントが発生したときにオブザーバーに通知します。
  • サブジェクトは、各オブザーバーに通知するために、オブザーバー コレクションを格納し、イベントが発生したときにそれを反復処理することで、これを処理します。
  • オブザーバー パターンは、オブザーバーをサブジェクトに登録します。
  • 複数のオブザーバーがいる場合があります。サブジェクトは、登録済みオブザーバーのリストを保持する必要があり、イベントが発生すると、すべての登録済みオブザーバーを起動 (通知を提供) します。
  • オブザーバーが不要な場合は、登録解除も可能です。

例 1. ローンの金利通知システム

$loan = new Loan("Mortage", "Citi Bank", 20.5);
$loan->attach(new Online());
$loan->attach(new SMS());
$loan->attach(new Email());

echo "<pre>";
$loan->setIntrest(17.5);

出力

Online    : Post online about modified Intrest rate of : 17.50
Send SMS  : Send SMS to premium subscribers : 17.50
Send Email: Notify mailing list : 17.50

例 2. シンプルなユーザー登録モニター

$users = new Users();

new Audit($users);
new Logger($users);
new Security($users);

$users->addUser("John");
$users->addUser("Smith");
$users->addUser("Admin");

出力

Audit    : Notify Audit about John
Log      : User John Create at Wed, 12 Dec 12 12:36:46 +0100
Audit    : Notify Audit about Smith
Log      : User Smith Create at Wed, 12 Dec 12 12:36:46 +0100
Audit    : Notify Audit about Admin
Log      : User Admin Create at Wed, 12 Dec 12 12:36:46 +0100
Security : Alert trying to create Admin

オブザーバー デザイン パターンの利点: 主な利点は、オブザーバーとオブザーバブルと呼ばれるオブジェクト間の疎結合です。サブジェクトはオブザーバーのリストを知っているだけで、オブザーバーがどのように実装されているかは気にしません。すべてのオブザーバーは、ブロードキャスト通信として単一のイベント呼び出しでサブジェクトから通知されます。

オブザーバー デザイン パターンの欠点:

  • 欠点は、何らかの問題が発生した場合、デバッグが非常に困難になることです。これは、制御の流れがオブザーバーとオブザーバブルの間で暗黙的に行われるため、オブザーバーが起動することを予測でき、オブザーバー間にチェーンがある場合はデバッグがより複雑になることです。
  • もう 1 つの問題は、大規模なオブザーバーを扱う場合のメモリ管理です。

共通クラス

abstract class Observable implements SplSubject {
    protected $_observers = [];

    public function attach(SplObserver $observer) {
        $id = spl_object_hash($observer);
        $this->_observers[$id] = $observer;
    }

    public function detach(SplObserver $observer) {
        $id = spl_object_hash($observer);

        if (isset($this->_observers[$id])) {
            unset($this->_observers[$id]);
        }
    }

    public function notify() {
        foreach ( $this->_observers as $observer ) {
            $observer->update($this);
        }
    }
}



abstract class Observer implements SplObserver {
    private $observer;

    function __construct(SplSubject $observer) {
        $this->observer = $observer;
        $this->observer->attach($this);
    }
}

サンプル クラスの読み込み

class Loan extends Observable {
    private $bank;
    private $intrest;
    private $name;

    function __construct($name, $bank, $intrest) {
        $this->name = $name;
        $this->bank = $bank;
        $this->intrest = $intrest;
    }

    function setIntrest($intrest) {
        $this->intrest = $intrest;
        $this->notify();
    }

    function getIntrest() {
        return $this->intrest;
    }
}

class Online implements SplObserver {

    public function update(SplSubject $loan) {
        printf("Online    : Post online about modified Intrest rate of : %0.2f\n",$loan->getIntrest());
    }
}

class SMS implements SplObserver {

    public function update(SplSubject $loan) {
        printf("Send SMS  : Send SMS to premium subscribers : %0.2f\n",$loan->getIntrest());
    }
}

class Email implements SplObserver {

    public function update(SplSubject $loan) {
        printf("Send Email: Notify mailing list : %0.2f\n",$loan->getIntrest());
    }
}

ユーザー登録のサンプル クラス

class Users extends Observable {
    private $name;

    function addUser($name) {
        $this->name = $name;
        $this->notify();
    }

    function getName() {
        return $this->name;
    }
}
class Audit extends Observer {

    public function update(SplSubject $subject) {
        printf("Audit    : Notify Autify about %s\n", $subject->getName());
    }
}
class Logger extends Observer {

    public function update(SplSubject $subject) {
        printf("Log      : User %s Create at %s\n", $subject->getName(),date(DATE_RFC822));
    }
}
class Security extends Observer {
    public function update(SplSubject $subject) {
        if($subject->getName() == "Admin")
        {
            printf("Security : Alert trying to create Admin\n");
        }
    }
}
于 2012-12-12T12:11:46.850 に答える
14

それは非常に単純です: サブジェクト/オブザーバー パターンはイベント システムには役に立ちません。

オブザーバー パターンは、「これは X によって更新されました」とは言えません。代わりに、単に更新されたと表示されます。イベント システムに活用できる柔軟なメディエーター クラスを実際に作成しました。ニーズによっては、より厳密な API が役立つ場合もありますが、それを参考にしてください。

では、サブジェクト/オブザーバー パターンが役立つのはどのような場合でしょうか。

一部のオブジェクトが変更されたために GUI を更新する場合、これはかなり一般的なパターンです。何が変更されたのか、なぜ変更されたのかを知る必要はありません。更新する必要があるだけです。PHP コードは HTML に直接結び付けられていないため、HTTP の性質はこの特定のパターンにはあまり適していません。更新するには、新しいリクエストを作成する必要があります。

つまり、Subject/Observer パターンは、PHP ではあまり役に立ちません。instanceofさらに、適切なサブジェクト タイプを取得するために使用する必要があるため、インターフェイスはそれほど有用ではありません。私は自分のインターフェースを書くだけで、それを扱いません。

于 2012-12-16T16:55:30.253 に答える
7

これらの 2 つのインターフェイスには魔法の機能が関連付けられていないため、これらを実装しても何も起こりません。それらは実際には参照目的でのみ使用されます。など、このような他の PHP 内部インターフェースがありますSeekableIterator。このメソッドには特別な機能はなくseek、自分で実装する必要があります。

Traversable特別な機能を備えたPHP 内部インターフェース などがありますが、これはSplSubjectandには当てはまりません。これはSplObserver基本的に、Observer パターンの実装用に提案されたインターフェースにすぎません。

が起こったのかというと、その情報は抽象的ではないため、インターフェースの一部ではありません。実装するのはあなた次第です。

interface Event extends SplSubject {
   public function getEventData();
}

class MyEvent implements Event {
   //MySubject implementation above
   public function getEventData() {
      return "this kind of event happened";
   }
}

Eventまた、インターフェイスを完全に無視するか、instanceofチェック (醜い) を使用して、メソッドに渡される「サブジェクト」の種類を確認することもできます。

実際の例については、このリンクで提供されていますが、 SplObserver/の使用SplSubjectは厳密には必要ありません。結局のところ、それらは単なるインターフェースです。基本的に、ExceptionHandlerサブジェクト クラスといくつかのオブザーバー ( Mailer. を使用できset_exception_handler(array($handler, 'notify'));、スローされた例外はすべてのオブザーバーに通知されます (たとえばMailer、キャッチされた例外に関する電子メールを送信します。他のメソッド/メンバーから例外を取得する必要がありますExceptionHandler)。

編集:update別の引数を使用してイベントを別のオブジェクトとして渡すことを計画しているコメントからわかります。これで問題ないと思いますが、私の提案は、Subject と Event の概念を分離せず、Subject にイベント データを含めるか、イベント データ自体にする機能を与えることです。受け取るイベント オブジェクトが null でないことを確認する必要があります。

于 2012-12-11T22:38:45.273 に答える
1

他のインターフェイスと同様に、実装するまでは役に立ちません。それらを実装することで、イベント駆動型アプリケーションを作成できます

10 個の関数を実行する必要があるイベント「applicationStart」があるとします。

function applicationStart() {
   // Some other logic 
   fnCall1();
   fnCall2();
   fnCall3();
   fnCall4();
   fnCall5();
   fnCall6();
   fnCall7();
   fnCall8();
   fnCall9();
   fnCall10();
   // Some other logic 
}

ここで、この関数をテストする必要があると想像してください。これにより、他の 10 個の関数すべてに対する依存関係がトリガーされます。

SplSubject/SplObserver を使用する場合:

function applicationStart() {
    // Logic
    $Subject->notify();
    // Logic
}

テストするときは、イベントをトリガーすることを確認する必要があります。他の機能の実行なし。

Plus コードは、そこに属さないビジネス ロジックで汚染しないため、よりきれいに見えます。トリガーを簡単に追加できる便利な場所

于 2012-12-12T02:01:56.830 に答える