java – 观察者模式:内部注册与外部注册

注册观察者的最佳选择是什么?我没有找到关于这个主题的任何内容.讨论了大多数“推与拉”,但也有一些注册观察者的选项.

public static void main(String[] args)
{
    Subject subject = new ConcreteSubject();
    // External registration
    Observer observerExternal = new ConcreteObserverExternal();
    subject.registerObserver(observerExternal);
    // Internal registration, option 1
    Observer observerInternal1 = new ConcreteObserverInternal1(subject);
    // Internal registration, option 2
    ConcreteObserverInternal2 observerInternal2 = new ConcreteObserverInternal2(subject);
}

interface Observer
{
    void inform();
}
class ConcreteObserverExternal implements Observer
{
    @Override
    public void inform()
    {
        // do sth.
    }
}
class ConcreteObserverInternal1 implements Observer
{
    public ConcreteObserverInternal1(Subject subject)
    {
        subject.registerObserver(this);
    }

    @Override
    public void inform()
    {
        // do sth.
    }
}
class ConcreteObserverInternal2
{
    public ConcreteObserverInternal2(Subject subject)
    {
        subject.registerObserver(() -> inform());
    }

    private void inform()
    {
        // do sth.
    }
}
interface Subject
{
    void registerObserver(Observer obs);

    void unregisterObserver(Observer obs);
}
class ConcreteSubject implements Subject
{
    @Override
    public void registerObserver(Observer obs)
    {
        // register
    }

    @Override
    public void unregisterObserver(Observer obs)
    {
        // unregister
    }

    private void foo()
    {
        // ...
        notifyObservers();
    }

    private void notifyObservers()
    {
        // notify observers
    }
}

以下是我的代码中的三种情况:

>观察员在程序启动时注册,永远不会注册.在这种情况下,所有3个选项都是可能的.
>观察者在某处注册,并且在发生某些外部事件时需要取消注册.观察者不知道这个外部事件,显然它必须在外部注册(选项1).
>观察者在某处注册,并且在发生某些外部事件时需要取消注册.观察者知道发生了什么,因为它也是这个外部事件的观察者.在这种情况下,所有3个选项都是可能的.

在所有3个选项都可能的情况下,从OO和清洁代码的角度来看哪一个是最好的?

以下列出了我认为每个选项都有的优缺点.

1.外部登记

优点:
  – 观察者的构造函数中的参数较少.
  – 不需要抽象主语来促进主语和观察者之间的松散耦合.

缺点:
  – 不要忘记在客户端代码中注册观察者.
  – 客户代码负责注册.

中性:
  – 观察者有一个额外的公共方法.
  – 观察者可以通过客户端代码注册/取消注册.

2.内部注册,选项1:具体观察者实现Observer接口

优点:
  – 观察员负责注册.
  – 注册不能忘记,因为一个人被迫将主题传递给观察者的构造函数.

缺点:
  – 观察者的构造函数中的另一个参数.

中性:
  – 观察者有一个额外的公共方法.
  – 观察者可以通过客户端代码注册/取消注册.
  – 观察者可以注册/注销自己.

3.内部注册,选项2:具体观察者不实现Observer接口

优点:
  – 观察员负责注册.
  – 注册不能忘记,因为一个人被迫将主题传递给观察者的构造函数.
  – 观察者没有可能被与“主体通知观察者”无关的任何内容滥用的其他公共方法.

缺点:
  – 观察者的构造函数中的另一个参数.

中性:
  – 观察者只能注册/注销自己.

最佳答案 鉴于您在“外部”和“内部”注册之间提出的细微差别,看起来似乎没有一个正确的答案.不过,我会试试.

我更喜欢“外部”注册其他两个,原因有两个:

>观察员对这些主题一无所知;即它们相当分离.例如,我可以将一个观察者附加到多个主题,并且没有人必须更改.
>更符合单一责任原则.观察者只关心在得到通知时需要做什么.它不关心注册/取消注册任何人.

点赞