2012-11-09 4 views
2

일부 디자인 패턴을 가지고 노는 중이며 SPL의 옵저버 패턴을 사용하여 예제를 만들고 싶습니다. 옵저버와 피사체를 완전히 일반화하는 것은 이치에 맞지 않기 때문에 필자는 인터페이스를 확장하여 응용 프로그램을보다 특수하게 만들려고했습니다. 문제는 아래 코드를 실행하면 "DataAccess :: update()가 SplObserver :: update()"와 호환되어야한다는 오류가 발생한다는 것입니다.확장 인터페이스를 구현하려면 어떻게해야합니까?

나는 메소드 서명을 인터페이스와 일치하도록 전환함으로써이 코드가 오류없이 실행될 수 있음을 알고 있습니다. 제 질문은 이것입니다 : 서명에서 정의 된 클래스의 하위를 허용하지 않는 이유는 무엇입니까? 아래, ModelObserver는 SplObserver이고 Model은 SplSubject입니다. 나는 이것이 작동 할 것으로 기대했을 것이다. 내가 놓친 게 있니?

참고로 인터페이스에 정의 된대로 명시적인 메서드 서명을 사용할 수 있으며 동일한 작업을 수행하기 위해 내 코드 논리에 instanceof 키워드를 사용할 수 있다는 것을 알고 있습니다. 나는 더 우아한 해결책을 찾기를 바랐다. 감사! 자녀를 Model 받아 DataAccess::update() 제한

<?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; 

답변

2

이 인터페이스의 계약을 나누기. 진정한

, 모든 Model 객체는 클래스 SplSubject의, 그러나 모든 SplSubject 클래스 Model의이다. 인터페이스는 구현 클래스가 인터페이스가 지원하는 모든 것을 지원한다는 것을 보증하는 계약입니다.

코드가 작동하면 DataAccess::update() 메서드가 Model 하위 클래스로 제한되고 더 넓은 부모 클래스는 SplSubjects이 아닙니다. 인터페이스에 정의 된 메서드에 전달 된 매개 변수의 범위를 좁힐 수 없습니다.

Model 클래스에 public $foo 속성을 추가했다고 가정 해 보겠습니다. 허용 된 경우 해당 $foo 속성을 사용하는 DataAccess::update() 메서드에서 사용할 수 있습니다. 누군가 SplSubjects$foo 속성이없는 OddModel 자손으로 가져올 수 있습니다. 더 이상 OddModelOddModel에 입력하면 OddModelDataAccess::update() 함수로 전달할 수 없습니다.

이것은 인터페이스의 배후에있는 전체 개념으로, 인터페이스에서 정의 된 것을 100 % 지원한다는 데 동의합니다. 이 경우 사용자 인터페이스는 말한다 : 당신이 저를 구현하는 경우

, 당신은 SplSubject

당신은 계약을 깰 수있는 인터페이스 시도의 구현에있어 확장 모든 SplSubject 또는 클래스에 동의해야합니다.

+0

의견에 감사드립니다. 이 경우 모든 SplObserver 객체가 반드시 모델을 처리 할 수있는 것은 아니기 때문에 전달할 매개 변수의 범위를 좁힐 수 있습니다. 상속을 통해 인터페이스의 범위를 좁힐 수 없다면, 내가 생각할 수있는 차선책은 메서드 자체 내에서 SplObserver 및 SplSubject의 인스턴스를 테스트하고 호환되는 관찰자 또는 대상이 아닌 경우 예외를 throw하는 것입니다. 동의 하시겠습니까, 아니면 제가 빠진 것이 있습니까? –

+0

... 메소드를 재정의하는 대신'DataAccess' 내부에 검사를 간단히 추가 할 수 있습니다. 뭔가 :'if (! $ subject instanceOf Model) 새로운 DataAccessException ('유효한 모델 대상이 아닙니다'); 또는 이와 비슷한 것을 던져라. –

+0

@JoshuaKaiser 당신이 구현 한 메소드 안에 원하는 것을 할 수 있지만, 전체적인 아이디어는 인터페이스를 완벽하게 지원하는 것입니다. 인터페이스 정의에서 요구하는 모든 기능을 완벽하게 수행하지 못하는 인터페이스를 구현하는 이유에 대해 질문해야합니다. 인터페이스는 약속입니다. 약속을 지키지 못하면 인터페이스를 사용하지 마십시오. 모든 SplObserver가 모델을 처리하는 방법은 무엇입니까? 모델은 SplSubject 유형입니다 - 그들은 단지 잘 처리하지 못할 수도 있습니다. 어떤 경우에는 확실히 예외를 throw합니다. – Ray

관련 문제