大话设计模式读书笔记——观察者模式

文章通过逐步演进的方式展示了如何使用观察者模式降低代码的耦合度,从最初的双向耦合版本,到使用抽象类解耦,再到完全解耦的观察者模式实现,最后介绍了如何利用Java内置的Observer接口进行优化。整个过程强调了开放-封闭原则和依赖倒置原则的应用。
摘要由CSDN通过智能技术生成


1. 需求

假如公司里的员工都在偷偷炒股,为了防止老板发现,现在需要开发一个摸鱼系统,要求前台小姐发现老板回来之后要及时通知大家。

2. 代码版本1.0(双向耦合版)

2.1 UML类图

在这里插入图片描述

2.2 前台小姐类

//前台秘书类
public class Secretary {
    protected String name;
    public Secretary(String name){
        this.name = name;
    }

    //同事列表
    private ArrayList<StockObserver> list = new ArrayList<>();
    private String action;

    //增加同事(有几个同事需要前台通知,就新增几个对象)
    public void attach(StockObserver observer){
        list.add(observer);
    }

    //通知
    public void notifyEmployee(){
        //待老板来了,就给所有登记过的同事发通知
        for (StockObserver item : list) {
            item.update();
        }
    }

    //得到状态
    public String getAction(){
        return this.action;
    }
    //设置状态(就是设置具体通知的话)
    public void setAction(String value){
        this.action = value;
    }

}

2.3 看股票类

public class StockObserver {
    private String name;
    private Secretary sub;
    public StockObserver(String name,Secretary sub){
        this.name = name;
        this.sub = sub;
    }

    public void update(){
        System.out.println(this.sub.name+":"+this.sub.getAction()+"!"+this.name+",请关闭股票行情,赶紧工作。");
    }
}

2.4 客户端类

        //前台小姐
        Secretary secretary = new Secretary("前台小姐");

        //看股票的同事
        StockObserver employee1 = new StockObserver("同事1", secretary);
        StockObserver employee2 = new StockObserver("同事2", secretary);

        //前台登记下两个同事
        secretary.attach(employee1);
        secretary.attach(employee2);

        //当发现老板回来时
        secretary.setAction("老板回来了");
        //通知两个同事
        secretary.notifyEmployee();

2.5 输出结果

在这里插入图片描述

2.6 弊端

由此看出,前台小姐类和看股票类的耦合度过高,而且实际情况中所有同事都不一定在同一件事上摸鱼。
为了遵循开放–封闭 和 依赖倒置原则,应该让程序都依赖抽象,而不是相互依赖。

3. 代码版本2.0(解耦版1)

3.1 UML 类图

在这里插入图片描述

3.2 抽象观察者类

public abstract class Observer {
    protected String name;
    protected Secretary sub;
    public Observer(String name,Secretary sub){
        this.name = name;
        this.sub = sub;
    }
    public abstract void update();
}

3.3 具体观察者类

public class StockObserver extends Observer{
    public StockObserver(String name, Secretary sub) {
        super(name, sub);
    }

    @Override
    public void update() {
        System.out.println(super.sub.name+":"+super.sub.getAction()+"!"+super.name+",请关闭股票行情,赶紧工作!");
    }
}
public class NBAObserver extends Observer{
    public NBAObserver(String name, Secretary sub) {
        super(name, sub);
    }

    @Override
    public void update() {
        System.out.println(super.sub.name+":"+super.sub.getAction()+"!"+super.name+",请关闭NBA直播,赶紧工作!");
    }
}

3.4 前台小姐类

//前台秘书类
public class Secretary {
    protected String name;
    public Secretary(String name){
        this.name = name;
    }

    //同事列表
    private ArrayList<Observer> list = new ArrayList<>();
    private String action;

    //增加同事(有几个同事需要前台通知,就新增几个对象)
    public void attach(Observer observer){
        list.add(observer);
    }

    //通知
    public void notifyEmployee(){
        //待老板来了,就给所有登记过的同事发通知
        for (Observer item : list) {
            item.update();
        }
    }

    //得到状态
    public String getAction(){
        return this.action;
    }
    //设置状态(就是设置具体通知的话)
    public void setAction(String value){
        this.action = value;
    }

}

3.5 弊端

在具体观察者类中,与前台小姐类耦合了,应该把“前台小姐类”抽象出来。

4. 代码版本3.0(解耦版2,观察者模式实现)

4.1 UML类图

在这里插入图片描述

4.2 抽象通知者类

抽象通知者,可以是接口,也可以是抽象类。

public abstract class Subject {
    protected String name;
    public Subject(String name){
        this.name = name;
    }

    //同事列表
    private ArrayList<Observer> list = new ArrayList<>();
    private String action;

    //增加同事(有几个同事需要前台通知,就新增几个对象)
    public void attach(Observer observer){
        list.add(observer);
    }

    //减少同事
    public void detach(Observer observer){
        list.remove(observer);
    }

    //通知
    public void notifyEmployee(){
        //待老板来了,就给所有登记过的同事发通知
        for (Observer item : list) {
            item.update();
        }
    }

    //得到状态
    public String getAction(){
        return this.action;
    }
    //设置状态(就是设置具体通知的话)
    public void setAction(String value){
        this.action = value;
    }
}

4.3 具体通知者类

具体的通知者类可能是前台秘书,也可能是老板,它们也许有各自的一些方法,但对于通知者来说,它们是一样的,所以它们都去继承这个抽象类Subject。

public class Boss extends Subject{
    public Boss(String name) {
        super(name);
    }

    //拥有自己的属性和方法
}

public class Secretary extends Subject{
    public Secretary(String name) {
        super(name);
    }

    //拥有自己的属性和方法
}

4.4 抽象观察者类

public abstract class Observer {
    protected String name;
    protected Subject sub;
    public Observer(String name, Subject sub){
        this.name = name;
        this.sub = sub;
    }
    public abstract void update();
}

4.5 具体观察者类

public class NBAObserver extends Observer {
    public NBAObserver(String name, Subject sub) {
        super(name, sub);
    }

    @Override
    public void update() {
        System.out.println(super.sub.name+":"+super.sub.getAction()+"!"+super.name+",请关闭NBA直播,赶紧工作!");
    }
}
public class StockObserver extends Observer {
    public StockObserver(String name, Subject sub) {
        super(name, sub);
    }

    @Override
    public void update() {
        System.out.println(super.sub.name+":"+super.sub.getAction()+"!"+super.name+",请关闭股票行情,赶紧工作!");
    }
}

4.6 客户端类

        Subject boss1 = new Boss("老板1");

        //看股票的同事
        Observer employee1 = new StockObserver("同事1",boss1);
        Observer employee2 = new StockObserver("同事2",boss1);
        //看NBA的同事
        Observer employee3 = new NBAObserver("同事3",boss1);

        //老板登记下三个同事
        boss1.attach(employee1);
        boss1.attach(employee2);
        boss1.attach(employee3);

        boss1.detach(employee1);//同事1其实没有被通知到,所以减去

        //老板回来
        boss1.setAction("我是老板,我回来了");
        //通知两个同事
        boss1.notifyEmployee();

4.7 输出结果

老板1:我是老板,我回来了!同事2,请关闭股票行情,赶紧工作!
老板1:我是老板,我回来了!同事3,请关闭NBA直播,赶紧工作!

5. 观察者模式

5.1 概念

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

5.2 使用场景

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
而观察者模式的关键对象是主题Subject观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。
总结得出观察者模式的使用场景如下:

  1. 当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变
  2. 解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

5.3 案例代码

5.3.1 UML 类图

在这里插入图片描述

5.3.2 抽象通知者类

Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对
象。

public abstract class Subject {
   private ArrayList<Observer> list = new ArrayList<Observer>();//针对抽象的Observer编程

    //增加观察者
    public void attach(Observer observer){
        list.add(observer);
    }
    //减少观察者
    public void detach(Observer observer){
        list.remove(observer);
    }
    //通知观察者
    public void notifyObserver(){
        for (Observer item : list) {
            item.update();
        }
    }
    protected String subjectState;
    public String getSubjectState(){
        return this.subjectState;
    }
    public void setSubjectState(String val){
        this.subjectState = val;
    }
}

5.3.3 抽象观察者类

Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫作更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个update方法,这个方法叫作更新方法。

public abstract class Observer {
    public abstract void update();
}

5.3.4 具体通知者类

ConcreteSubject类,叫作具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。

public class ConcreteSubject extends Subject{
    //具体的通知者方法
}

5.3.5 具体观察者类

ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。

public class ConcreteObserver extends Observer{
    private String name;
    private Subject sub;

    public ConcreteObserver(String name,Subject sub){
        this.name = name;
        this.sub = sub;
    }

    @Override
    public void update() {
        System.out.println("观察者"+this.name+"的新状态"+this.sub.getSubjectState());
    }
}

5.3.6 客户端类

        Subject subject = new ConcreteSubject();
        subject.attach(new ConcreteObserver("NameX",subject));
        subject.attach(new ConcreteObserver("NameY",subject));
        subject.attach(new ConcreteObserver("NameZ",subject));
        subject.setSubjectState("ABC");

        subject.notifyObserver();

6. Java内置接口实现

现实编程中,具体的观察者完全有可能是风马牛不相及的类,但它们都需要根据通知者的通知来做出update的操作,所以让它们都实现一个接口就可以实现这个想法了。
正好,Java已经为观察者模式准备好了相关的接口和抽象类了 有 了 这 些 Java 内 置 代 码 的 支 持 , 你 只 需 要 扩 展 或 继 承Observable,并告诉它什么时候应该通知观察者,就OK了,剩下的事Java会帮你做。

6.1 代码版本4.0(Java内置接口实现)

6.1.1 UML类图

在这里插入图片描述

6.1.2 具体通知者板类

由 于 已 经 有 了 Observable 实 现 的 各 种 方 法 , 比 如 加 观 察 者( addObserver ) 、 减 观 察 者 ( deleteObserver ) 、 通 知 观 察 者(notifyObservers)等。所以Boss类继承了Observable,已经无须再实现这些代码了。
Boss继承Observable类,当addObserver添加一些观察者后,它在setAction里是这样工作的:调用setChanged方法,标记状态已经改变,然后调用notifyObservers方法来通知观察者。

public class Boss extends Observable {
    protected String name;
    private String action;

    public Boss(String name){
        this.name = name;
    }

    //得到状态
    public String getAction(){
        return this.action;
    }
    //设置状态(就是设置具体通知的话)
    public void setAction(String val){
        this.action = val;
        super.clearChanged();
        super.notifyObservers();
    }
}

6.1.3 具体观察者类

public class StockObserver implements Observer {
    protected String name;
    public StockObserver(String name){
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        Boss b = (Boss)o;//需要拆箱将Observable对象转换成Boss
        System.out.println(b.name+":"+b.getAction()+"!"+this.name+",请关闭股票行情,赶紧工作");
    }
}

6.1.4 客户端

        Boss boss1 = new Boss("老板1");

        //看股票的同事
        Observer employee1 = new StockObserver("同事1");
        Observer employee2 = new StockObserver("同事2");
        Observer employee3 = new StockObserver("同事3");

        //老板登记两个同事
        boss1.addObserver(employee1);
        boss1.addObserver(employee2);

        boss1.deleteObserver(employee3);//同事3其实没有通知到,所以减去

        //老板回来
        boss1.setAction("我是老板。我回来了");

6.1.5 不足

这个版本的代码仍然存在不足,在StockObserver类中,竟然出现了Boss,具体类中耦合了具体类了,这就没有针对接口编程了。下面给出改良版

6.1.6 改良版UML类图

在这里插入图片描述

6.1.7 改良版抽象通知者类和具体通知者类

public class Subject extends Observable {
    protected String name;
    private String action;

    public Subject(String name){
        this.name = name;
    }

    //得到状态
    public String getAction(){
        return this.action;
    }
    //设置状态(就是设置具体通知的话)
    public void setAction(String val){
        this.action = val;
        super.clearChanged();
        super.notifyObservers();
    }
}
public class Boss extends Subject {
    public Boss(String name){
        super(name);
    }
}

6.1.8 改良版具体观察者类

public class StockObserver implements Observer {
    protected String name;
    public StockObserver(String name){
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        Subject b = (Subject) o;//需要拆箱将Observable对象转换成Boss
        System.out.println(b.name+":"+b.getAction()+"!"+this.name+",请关闭股票行情,赶紧工作");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

头盔程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值