学习设计模式笔记之观察者模式

一、定义

定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。

例如:报纸订阅服务,出版者和订阅者。

二、角色

1、Subject,主题接口,对象使用此接口注册为观察者,或者把自己从观察者中删除。

2、Observer,观察者接口,一个主题可以有多个观察者。所有潜在的观察者必须实现观察者接口,这个接口只有update()一个方法,当主题发生改变时它被调用。

3、ConcreteObserver,具体的观察者,可以是实现观察者接口的任意类。观察者必须注册具体主题,以便接收更新。

4、ConcreteSubject,一个具体主题总是实现主题接口,除了注册和撤销方法之外,具体主题还实现了notifyObserver()方法,此方法用于在状态改变时更新所有当前观察者。具体主题也可能有设置和获取状态的方法。

三、松耦合的威力

当两个对象之间松耦合,它们依旧可以交互,但是不太清楚彼此的细节。

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的相互依赖降到了最低。

四、注意点

1、观察者可以观察多个目标,但是更新方法需要实现多个,一个更新方法对应一个目标。避免业务混淆。

2、一个目标可以有多个观察者,通过遍历观察者进行通知。

3、观察者依赖于目标对象,注册时需要将观察者对象引用添加至目标对象的属性中。

4、设置值可以先判断是否更新了值,再去更新状态,然后确定是否设置值和调用方法。

5、可以将目标注入至Spring的IOC容器中,再在Spring中注入一个注册管理类,进行观察者的注册管理。再bean初始化时,将观察者注入至目标对象中。

6、多个观察者模型对象中不能有执行顺序依赖。

7、拉模型,update更新方法中传递目标实现对象。推模型,update更新方法中只传递需要的数据信息。一般使用拉模型,方便获取目标对象的所有数据,易于业务扩展。

五、命名建议(发布订阅模式)

1、目标接口的定义,建议在名称后面跟subject;

2、观察者接口的定义,建议在名称后面跟observer;

3、观察者接口的更新方法,建议名称为update。

4、实现类前面建议加,concrete。

六、观察者模式的调用顺序示意图

1、维护接口,创建目标对象,创建观察者对象,向目标对象注册观察者对象。

2、运行接口,改变目标对象的状态,通知所有注册的观察者对象进行相应的处理,回调目标对象,获取相应的数据。

七、思路笔记

1、创建主题接口,提供三个方法(添加方法、删除方法、通知方法),一个集合属性(订阅者的接口引用集合)。

2、主题接口实现类,实现主题接口的类便是需要保存值的对象模型,其中进行数据通知的具体方法操作,当这个类中的属性参数变化时,便出发notify方法。

3、notify方法中,遍历订阅者接口引用对象,使用订阅者对象调用update更新方法,可传入主题接口实现类对象,拉模型方式。

4、拉模型方式中,订阅者可以根据自身需要,获取主题接口实现类中的数据信息,进行业务操作。

5、创建观察者接口,观察者接口只提供一个公共的更新方法,update(),参数可以是主题接口实现类对象。

6、观察者接口实现类,实现update(),在其中根据获取主题接口实现类中的状态参数值进行业务实现操作。

优点

1、观察者与目标解耦。

2、实现了动态联动,只需要添加注册观察者即可。

3、支持广播通信

缺点

1、每个都需要调用update,可能引起不必要的性能消耗。

八、示例代码块

1、观察者注册管理器

package com.fly.simpletools.service.observer;

import com.fly.simpletools.service.observer.observer.ConcreteOneObserver;
import com.fly.simpletools.service.observer.observer.ConcreteTwoObserver;
import com.fly.simpletools.service.observer.subject.Subject;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @author Mr_Fei
 * @description 观察者注册器
 * @date 2020-07-19 16:16
 */
@Component
public class RegisterObserverHandler {

    @Resource
    private Subject subject;

    @Bean
    private void register() {
        //注册观察者1
        subject.addObserver(new ConcreteOneObserver());
        //注册观察者2
        subject.addObserver(new ConcreteTwoObserver());
    }
}

2、主题接口

package com.fly.simpletools.service.observer.subject;

import com.fly.simpletools.service.observer.observer.Observer;

/**
 * @author Mr_Fei
 * @description 自己实现的主题接口
 * @date 2020-07-19 16:18
 */
public interface Subject {

    /**
     * @param observer 观察者
     * @author Mr_Fei
     * @date 2020/7/19 16:35
     * @description 注册观察者接口
     */
    void addObserver(Observer observer);

    /**
     * @param observer 观察者
     * @author Mr_Fei
     * @date 2020/7/19 16:35
     * @description 移除观察者
     */
    void deleteObserver(Observer observer);

    /**
     * @author Mr_Fei
     * @date 2020/7/19 16:35
     * @description 通知观察者
     */
    void notifyObserver();

    /**
     * @author Mr_Fei
     * @date 2020/7/19 16:35
     * @description 设置改变状态
     */
    void setChanged();
}

3、主题实现类

package com.fly.simpletools.service.observer.subject;

import com.fly.simpletools.service.observer.observer.Observer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Mr_Fei
 * @description 主题实现类
 * @date 2020-07-19 16:22
 */
@Component
public class ConcreteSubject implements Subject {

    /**
     * 传递通知内容
     */
    private Object content;
    /**
     * 存放观察者的集合
     */
    private List<Observer> observers = new ArrayList<>();
    /**
     * 判断是否内容发生变化
     */
    private boolean changed = false;

    @Override
    public void addObserver(Observer observer) {
        if (observer != null) {
            this.observers.add(observer);
        }
    }

    @Override
    public void deleteObserver(Observer observer) {
        if (observer != null) {
            this.observers.remove(observer);
        }
    }

    @Override
    public void notifyObserver() {
        if (observers.isEmpty() || !changed) {
            return;
        }
        for (Observer observer : observers) {
            observer.update(this, content);
        }
        this.changed = false;
    }

    @Override
    public void setChanged() {
        this.changed = true;
    }

    public Object getContent() {
        return content;
    }

    /**
     * @param content 内容
     * @author Mr_Fei
     * @date 2020/7/19 16:36
     * @description set方法判断值是否发生改变,改变则通知观察者
     */
    public void setContent(Object content) {
        if (content != this.content) {
            this.setChanged();
        }
        this.content = content;
        this.notifyObserver();
    }

}

4、观察者接口

package com.fly.simpletools.service.observer.observer;

import com.fly.simpletools.service.observer.subject.Subject;

/**
 * @author Mr_Fei
 * @description 观察者接口
 * @date 2020-07-19 16:19
 */
public interface Observer {

    /**
     * @param subject 主题对象,拉方式参数
     * @param arg     消息内容,推方式参数
     * @author Mr_Fei
     * @date 2020/7/19 16:20
     * @description 更新方法
     */
    void update(Subject subject, Object arg);

}

5、观察者实现1

package com.fly.simpletools.service.observer.observer;

import com.fly.simpletools.service.observer.subject.Subject;
import com.fly.simpletools.service.observer.subject.ConcreteSubject;

/**
 * @author Mr_Fei
 * @description 观察者1
 * @date 2020-07-19 16:26
 */
public class ConcreteOneObserver implements Observer {

    @Override
    public void update(Subject subject, Object arg) {
        ConcreteSubject testSubject = (ConcreteSubject) subject;
        //进行业务操作
        System.out.println("TestOneObserverImpl收到主题通知消息内容:" + testSubject.getContent());
    }
}

6、观察者实现2

package com.fly.simpletools.service.observer.observer;

import com.fly.simpletools.service.observer.subject.Subject;
import com.fly.simpletools.service.observer.subject.ConcreteSubject;

/**
 * @author Mr_Fei
 * @description 观察者2
 * @date 2020-07-19 16:26
 */
public class ConcreteTwoObserver implements Observer {

    @Override
    public void update(Subject subject, Object arg) {
        ConcreteSubject testSubject = (ConcreteSubject) subject;
        //进行业务操作
        System.out.println("TestTwoObserverImpl收到主题通知消息内容:" + testSubject.getContent());
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42080073/article/details/107448108