문제

생산자/소비자 관계로 두 클래스를 디자인할 때 순환 종속성을 어떻게 피할 수 있습니까?여기서 ListenerImpl은 자체 등록/등록 취소를 위해 Broadcaster에 대한 참조가 필요하고, Broadcaster는 메시지를 보내기 위해 Listener에 대한 참조가 필요합니다.이 예제는 Java로 작성되었지만 모든 OO 언어에 적용할 수 있습니다.

public interface Listener {
  void callBack(Object arg);
}
public class ListenerImpl implements Listener {
  public ListenerImpl(Broadcaster b) { b.register(this); }
  public void callBack(Object arg) { ... }
  public void shutDown() { b.unregister(this); }
}
public class Broadcaster {
  private final List listeners = new ArrayList();
  public void register(Listener lis) { listeners.add(lis); }
  public void unregister(Listener lis) {listeners.remove(lis); }
  public void broadcast(Object arg) { for (Listener lis : listeners) { lis.callBack(arg); } }
}
도움이 되었습니까?

해결책

나는 그것이 원형 의존성 인 것을 보지 못한다.

듣는 사람은 아무것도 의존하지 않습니다.

ListenerImpl은 청취자와 방송사에 따라 다릅니다

방송사는 청취자에 의존합니다.

        Listener
       ^        ^
      /          \
     /            \
Broadcaster <--  ListenerImpl

모든 화살표는 리스너에서 끝납니다. 사이클이 없습니다. 그래서, 나는 당신이 괜찮다고 생각합니다.

다른 팁

OOP 언어가 있습니까? 확인. Clos의 10 분 버전이 있습니다.

방송 프레임 워크

(defclass broadcaster ()
  ((listeners :accessor listeners
              :initform '())))

(defgeneric add-listener (broadcaster listener)
  (:documentation "Add a listener (a function taking one argument)
  to a broadcast's list of interested parties"))

(defgeneric remove-listener (broadcaster listener)
  (:documentation "Reverse of add-listener"))

(defgeneric broadcast (broadcaster object)
  (:documentation "Broadcast an object to all registered listeners"))

(defmethod add-listener (broadcaster listener)
  (pushnew listener (listeners broadcaster)))

(defmethod remove-listener (broadcaster listener)
  (let ((listeners (listeners broadcaster)))
    (setf listeners (remove listener listeners))))

(defmethod broadcast (broadcaster object)
  (dolist (listener (listeners broadcaster))
    (funcall listener object)))

예제 서브 클래스

(defclass direct-broadcaster (broadcaster)
  ((latest-broadcast :accessor latest-broadcast)
   (latest-broadcast-p :initform nil))
  (:documentation "I broadcast the latest broadcasted object when a new listener is added"))

(defmethod add-listener :after ((broadcaster direct-broadcaster) listener)
  (when (slot-value broadcaster 'latest-broadcast-p)
    (funcall listener (latest-broadcast broadcaster))))

(defmethod broadcast :after ((broadcaster direct-broadcaster) object)
  (setf (slot-value broadcaster 'latest-broadcast-p) t)
  (setf (latest-broadcast broadcaster) object))

예제 코드

Lisp> (let ((broadcaster (make-instance 'broadcaster)))
        (add-listener broadcaster 
                      #'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
        (add-listener broadcaster 
                      #'(lambda (obj) (format t "I has object: ~A~%" obj)))
        (broadcast broadcaster 'cheezburger))

I has object: CHEEZBURGER
I got myself a CHEEZBURGER object!

Lisp> (defparameter *direct-broadcaster* (make-instance 'direct-broadcaster))
      (add-listener *direct-broadcaster*
                  #'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
      (broadcast *direct-broadcaster* 'kitty)

I got myself a KITTY object!

Lisp> (add-listener *direct-broadcaster*
                    #'(lambda (obj) (format t "I has object: ~A~%" obj)))

I has object: KITTY

불행히도 LISP는 대부분의 디자인 패턴 문제 (예 : 귀하와 같은)를 제거하여 해결합니다.

Herms의 대답과 달리 i 하다 루프를 참조하십시오. 종속성 루프가 아니며 AA 참조 루프입니다. Li는 B 객체를 보유하고 B 객체가 1 개의 배열을 유지합니다 (A) Li 객체 (). 그들은 쉽게 자유롭지 않으며, 가능한 경우 자유롭게되도록주의를 기울여야합니다.

하나의 해결 방법은 단순히 Li 객체가 방송사에게 약한 회의를 갖도록하는 것입니다. 이론적으로, 방송사가 사라졌다면 어쨌든 데드 레저를 할 것이 없으므로, 방해물은 단순히 방송사가 데려 오는 방송사가 있는지 확인하고 있다면 그렇게 할 경우.

나는 Java Dev가 아니라 다음과 같은 것입니다.

public class ListenerImpl implements Listener {
  public Foo() {}
  public void registerWithBroadcaster(Broadcaster b){ b.register(this); isRegistered = true;}
  public void callBack(Object arg) { if (!isRegistered) throw ... else ... }
  public void shutDown() { isRegistered = false; }
}

public class Broadcaster {
  private final List listeners = new ArrayList();
  public void register(Listener lis) { listeners.add(lis); }
  public void unregister(Listener lis) {listeners.remove(lis); }
  public void broadcast(Object arg) { for (Listener lis : listeners) { if (lis.isRegistered) lis.callBack(arg) else unregister(lis); } }
}

약한 참조를 사용하여 사이클을 깨뜨립니다.

보다 이 답변.

다음은 Lua의 예입니다. (나는 내 ​​자신의 것을 사용합니다. 이런 lib 여기에서는 코드의 'Object'에 대한 참조를 참조하세요.)

Mikael Jansson의 CLOS 예제와 마찬가지로 함수를 직접 사용할 수 있으므로 리스너를 정의할 필요가 없습니다('...' 사용에 유의하세요. 이는 Lua의 가변 인수입니다).

Broadcaster = Object:subclass()

function Broadcaster:initialize()
    self._listeners = {}
end

function Broadcaster:register(listener)
    self._listeners[listener] = true
end

function Broadcaster:unregister(listener)
    self._listeners[listener] = nil
end
function Broadcaster:broadcast(...)
    for listener in pairs(self._listeners) do
        listener(...)
    end
end

구현에 충실하여 다음은 제가 추측하는 어떤 동적 언어로도 작성될 수 있는 예입니다.

--# Listener
Listener = Object:subclass()
function Listener:callback(arg)
    self:subclassResponsibility()
end

--# ListenerImpl
function ListenerImpl:initialize(broadcaster)
    self._broadcaster = broadcaster
    broadcaster:register(this)
end
function ListenerImpl:callback(arg)
    --# ...
end
function ListenerImpl:shutdown()
    self._broadcaster:unregister(self)
end

--# Broadcaster
function Broadcaster:initialize()
    self._listeners = {}
end
function Broadcaster:register(listener)
    self._listeners[listener] = true
end
function Broadcaster:unregister(listener)
    self._listeners[listener] = nil
end
function Broadcaster:broadcast(arg)
    for listener in pairs(self._listeners) do
        listener:callback(arg)
    end
end
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top