大话设计模式学习笔记(14)——观察者模式(发布-订阅模式)

源码git地址 https://github.com/dlovetco/designMode

问题提出

同学们都在上自习课,老师没人所以开始纷纷玩起来。坐在门口的同学需要完成望风任务,即发现老师回来的时候要告诉全班同学不要玩了,继续上自习。要求用代码实现上述场景。

由上述问题,我们可以定义两个角色:望风的同学(小明),玩耍的同学(小刚和小红)。
先举一个简单的高耦合的例子。

高耦合代码
package observerMode;

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

public class ObserverMode {
    public static void main(String[] args) {
        Ming ming = new Ming();
        Student gang = new Student("小刚", "玩篮球", ming);
        Student hong = new Student("小红", "画画", ming);
        ming.addStudents(gang);
        ming.addStudents(hong);
        ming.seeTeacherComing();
    }
}

class Ming {

    private List<Student> students = new ArrayList<>();

    void addStudents(Student student) {
        students.add(student);
    }

    void seeTeacherComing() {
        students.forEach(Student::update);
    }

    String sendNotice() {
        return "小明说老师回来了";
    }
}

class Student {

    private String name;
    private String action;
    private Ming ming;

    public Student(String name, String action, Ming ming) {
        this.name = name;
        this.action = action;
        this.ming = ming;
    }

    void update() {
        System.out.println(ming.sendNotice() + name + "停止" + action + "认真学习");
    }
}

上述代码只是表现了最简单的一种情况,各位看官可以适当写的复杂一点(#^.^#)。这是最简单的代码,但也是耦合性最高的代码。
1. 首先没有抽象,直接把望风的同学定死了是小明,其他同学也已经定死了是小刚和小红(有可能不同的同学在做不同的事情)。没有体现依赖倒转原则
2. 因为有1的缺点,所以类与类之间必然存在高耦合(没有接口)。

观察者模式

package observerMode;

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

public class ObserverMode {
    public static void main(String[] args) {
        Ming ming = new Ming();
        Observer gang = new Student("小刚", "打篮球", ming);
        Observer hong = new Student("小红", "画画", ming);
        ming.addObserver(gang);
        ming.addObserver(hong);
        ming.seeTeacherComing();
    }
}

/**
 * 观察者 希望得到通知的人
 */
interface Observer {
    void update();
}

/**
 * 通知者 发出通知的人
 */
interface Informer {
    void seeTeacherComing();

    String sendNotice();
}

class Ming implements Informer {

    private List<Observer> observers = new ArrayList<>();

    void addObserver(Observer observer) {
        observers.add(observer);
    }

    void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void seeTeacherComing() {
        observers.forEach(Observer::update);
    }

    @Override
    public String sendNotice() {
        return "小明说老师回来了";
    }

}

class Student implements Observer {

    public Student(String name, String action, Informer informer) {
        this.name = name;
        this.action = action;
        this.informer = informer;
    }

    String name;
    String action;
    Informer informer;

    @Override
    public void update() {
        System.out.println(informer.sendNotice() + name + "停止" + action + "认真学习");
    }
}

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

就这个例子而言,我们把望风的同学抽象成通知者,上自习的同学抽象成观察者。而观察者的状态(玩还是自习)取决于通知者是否发出通知(老师是否回来了)。

plantuml

@startuml
interface Observer{
update()
}
interface Informer{
seeTeacherComing()
sendNotice()
}

Observer <|.. Student
Informer<--Student
class Student{
Informer informer
}

Informer <|.. Ming
Observer <-- Ming
class Ming{
List<Observer> observers
seeTeacherComing()
sendNotice()
}
@enduml

这里写图片描述

注:

书上使用抽象类,而我是用接口。所以uml图看起来会有所不同。

观察者模式的缺点
如果说每一个具体的观察者他们的接收到通知后应变的方法不同(方法名自然也都不一样,比如小刚是stopPlaying,小红是stopPainting),就不能够使用接口来实现观察者模式。在C#中可以使用委托来处理这个事情,而在java中,我们可以通过函数式接口来实现相应的功能。

使用java的函数式接口实现C#的委托

package observerMode;

import java.util.function.Supplier;

public class ObserverMode {
    public static void main(String[] args) {
        Informer ming = new Ming();
        Gang gang = new Gang(ming);
        Hong hong = new Hong(ming);
        ming.seeTeacherComing(() -> {
            gang.stopPlaying();
            hong.stopPainting();
            return null;
        });
    }
}

/**
 * 通知者 发出通知的人
 */
interface Informer {
    void seeTeacherComing(Supplier<?> supplier);

    String sendNotice();
}

class Ming implements Informer {

    @Override
    public void seeTeacherComing(Supplier<?> supplier) {
        supplier.get();
    }

    @Override
    public String sendNotice() {
        return "小明说老师回来了";
    }
}

class Gang {

    Informer informer;

    public Gang(Informer informer) {
        this.informer = informer;
    }

    void stopPlaying() {
        System.out.println(informer.sendNotice() + "小刚停止打篮球");
    }
}

class Hong {

    Informer informer;

    public Hong(Informer informer) {
        this.informer = informer;
    }

    void stopPainting() {
        System.out.println(informer.sendNotice() + "小红停止画画");
    }
}

这里就是把通知哪些同学的选择交给了客户端。通知者中不需要再有观察者的引用,减少了这两个角色的耦合。不过反过来加大了客户端的工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值