3

私はいくつかのデザインパターンで遊んでいて、SPLのオブザーバーパターンを使用して例を作成したいと思いました。オブザーバーとサブジェクトを完全に一般化することは意味がないため、インターフェイスを拡張して、手元のアプリケーションに固有のものにしたいと思いました。問題は、以下のコードを実行すると、「DataAccess :: update()はSplObserver :: update()のコードと互換性がある必要があります」などのエラーが発生することです。

メソッドのシグネチャをインターフェイスのシグネチャと一致するように切り替えることで、このコードをエラーなしで実行できることを知っています。私の質問はこれです:なぜそれは署名で定義されたクラスの子を許可しないのですか? 以下では、ModelObserverはSplObserverであり、ModelはSplSubjectです。私はこれがうまくいくと思っていたでしょう。私は何かが足りないのですか?

参考までに、インターフェイスで定義されている明示的なメソッドシグネチャを使用し、コードロジックでinstanceofキーワードを使用して同じことを実現できることはわかっています。もっとエレガントな解決策を見つけたいと思っていました。ありがとう!

<?php
interface ModelObserver extends SplObserver {
}

class DataAccess implements ModelObserver {

    /*
     * (non-PHPdoc) @see SplObserver::update()
     */
    public function update(Model $subject) {
        // TODO Auto-generated method stub
    }
}

// Just a generic model for the example
class Model implements SplSubject {
    private $_properties = array ();
    private $_observers = array ();

    /*
     * generically handle properties you wouldn't want to do it quite like this
     * for a real world scenario
     */
    public function __get($name) {
        return $this->_properties [$name];
    }
    public function __set($name, $value) {
        $this->_properties [$name] = $value;
    }
    public function __call($method, $args) {
        if (strpos ( $method, 'get' ) === 0) {
            $name = lcfirst ( str_replace ( 'get', '', $method ) );
            return $this->_properties [$name];
        }

        if (strpos ( $method, 'set' ) === 0) {
            $name = lcfirst ( str_replace ( 'set', '', $method ) );
            $this->_properties [$name] = $args [0];
            return $this;
        }
    }
    public function __toString() {
        return print_r ( $this, true );
    }

    /*
     * (non-PHPdoc) @see SplSubject::attach()
     */
    public function attach(ModelObserver $observer) {
        $this->_observers [] = $observer;
        return $this;
    }

    /*
     * (non-PHPdoc) @see SplSubject::detach()
     */
    public function detach(ModelObserver $observer) {
        if (in_array ( $observer, $this->_observers )) {
            $f = function ($value) {
                if ($value != $observer) {
                    return $value;
                }
            };
            $observers = array_map ( $f, $this->_observers );
        }
        return $this;
    }

    /*
     * (non-PHPdoc) @see SplSubject::notify()
     */
    public function notify() {
        foreach ($this->_observers as $observer) {
            $observer->update($this);
        }
    }
}

$da = new DataAccess();

$model = new Model ();
$model->setName ( 'Joshua Kaiser' )->setAge ( 32 )->setOccupation ( 'Software Engineer' )
    ->attach($da);

echo $model;
4

1 に答える 1

3

DataAccess::update()あなたの子供を受け入れるように制限するModelと、このインターフェースの契約が破られます。

確かに、すべてのModelオブジェクトは class ですが、すべてがclassSplSubjectであるとは限りません。インターフェイスは、実装クラスがインターフェイスがサポートするすべてのものをサポートすることを保証する契約です。SplSubjectModel

あなたのコードは、それが機能した場合、DataAccess::update()メソッドをModelサブクラスのみに制限し、より広い親クラスには制限しませんSplSubjects。インターフェイスで定義されたメソッドに渡されるパラメーターのスコープを狭めることはできません。

public $fooModel クラスにプロパティを追加したとします。許可されている場合は、DataAccess::update()メソッドでそのプロパティを使用できます$foo。誰かがやって来て、プロパティを持たないSplSubjects子供に手を差し伸べることができました。関数に を渡すことができなくなりました。OddModel$fooOddModelDataAccess::update()$fooOddModel

これがインターフェースの背後にある全体的な考え方です。それらを実装することにより、インターフェースによって定義されたものをサポートすることに 100% 同意することになります。この場合、あなたのインターフェースは次のように言います:

私を実装する場合はSplSubject、拡張するすべてまたはクラスを受け入れる必要がありますSplSubject

インターフェイスの実装は、契約を破ろうとしています。

于 2012-11-09T17:50:04.560 に答える