设计模式 --- 观察者模式

203 阅读4分钟

观察者模式属于行为设计模式,也叫发布订阅模式,在实际场景中应用很多,比如邮件订阅,微信公众号订阅等

观察者模式模板


观察者模式中需要两个角色,分别是观察者被观察者,他们两个的叫法也有很多,如生产者和消费者,发布者和订阅者,发布者和监听者,但本质都是一样的

下面给出观察者模式的具体代码

被观察者接口,定义了一系列操作观察者的方法,以及他的实现类

// 被观察者接口
public interface Observable{
	void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObserver(Message message);
}

// 被观察者实现类
public class ObservableImpl implements Observable{
	private List<Observer> list = new ArrayList<>();
    
    void registerObserver(Observer observer){
    	list.add(observer);
    }
    
    void removeObserver(Observer observer){
    	list.remove(observer);
    }
    
    void notifyObservers(String message){
    	list.foreach(item -> item.doSomeThing(message));
    }
    
    void doThings(){
    	System.out.println("被观察者做事情了");
        this.notifyObservers();
    }
}


观察者接口,以及实现类

// 观察者接口
public interface Observer{
    void handle(String message);
}


// 观察者1
public class Observer1 implements Observer{
	void handle(String message){
        // 观察者1的逻辑
    	System.out.println(message);
    }
}


// 观察者2
public class Observer2 implements Observer{
	void handle(String message){
        // 观察者2的逻辑
    	System.out.println(message);
    }
}


最后测试

// 测试
public class Demo{
	public static void main(String[] args){
        // 创建订阅者
    	Observer observer1 = new Observer1();
        Observer observer2 = new Observer2();
        
        // 注册订阅者
        Observable observable = new ObservableImpl();
        observable.registerObserver(observer1);
        observable.registerObserver(observer2);
        
        // 发布通知
        observable.notifyObservers(new Message);
    }
}






jdk 自带观察者模式接口


细心的人应该可以发现,被观察者职责并不单一,它既要处理自己的逻辑,又要有注册、删除、通知观察者的逻辑,应该抽象出一个父类,只处理被观察者的职责,而被观察者只需关心自己的逻辑,幸运的是,jdk 中已经有现成的被观察者父类, Observable ,并且还提供了观察者的接口 Observer ,接下来用 jdk 提供的父类和接口来实现一下观察者模式

想必大家对间谍一定不会陌生,在战争时期,间谍可以探查敌人的情报,知道敌人正在干什么,并作出相应的决策,接下来模拟的场景就是基于这个。

战国时期,韩非子和李斯处在不同的国家,立场不同,李斯想灭掉韩国,于是向韩非子身边安插了间谍,韩非子做什么,间谍都能及时的发现,报告给李斯,而李斯知道了韩非子正在干什么,就会报告给秦始皇,这里面,韩非子就是被观察者,而李斯则是观察者

首先创建人这个接口,因为韩非子也是人,他也和普通人一样要吃喝拉撒

// 人接口
public interface Person {
    void eat();

    void play();
}


接下来要创建韩非子这个人,并且他是被观察者,需要实现 jdk 提供的 Observable 接口

public class HanFeiZi extends Observable implements Person {

    @Override
    public void eat() {
        System.out.println("韩非子:开始吃饭了");
        super.setChanged();
        super.notifyObservers("韩非子在吃饭");
    }

    @Override
    public void play() {
        System.out.println("韩非子:开始打游戏了");
        super.setChanged();
        super.notifyObservers("韩非子在打游戏");
    }
}


然后就是实现李斯这个观察者了,观察者实现的是 Observer 接口,他里面就定义了一个 update 方法,对间谍发过来的信息进行处理

public class LiSi implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("李斯:观察到韩非子活动,开始向老板汇报...");
        System.out.println("李斯:报告秦老板," + arg);
        System.out.println("李斯:报告完毕");
    }
}

这里 update 方法还有一个参数 Observable 接口没有使用到,这个参数实际上就是 HanFeiZi 这个对象,只有它是被观察者,可以通过这个对象来添加观察者或者删除观察者操作,这里暂时用不到

当然,事实上,想要消灭韩非子的肯定不止一个人,这里就不一一实现了



最后就是进行测试了

public class Main {
    public static void main(String[] args) {
        HanFeiZi hanFeiZi = new HanFeiZi();
        LiSi liSi = new LiSi();

        hanFeiZi.addObserver(liSi);

        hanFeiZi.eat();
        hanFeiZi.play();
    }
}

结果

韩非子:开始吃饭了
李斯:观察到韩非子活动,开始向老板汇报...
李斯:报告秦老板,韩非子在吃饭
李斯:报告完毕
韩非子:开始打游戏了
李斯:观察到韩非子活动,开始向老板汇报...
李斯:报告秦老板,韩非子在打游戏
李斯:报告完毕





观察者模式注意事项


异步处理问题
**
被观察者作出动作,就要通知所有观察者,等一个观察者处理完后才能通知另一个观察者,如果某个观察者处理较慢,会拖累整个系统的运行速度。因此,最好的解决方案是用异步来处理观察者的逻辑,很多类似观察者模式的框架都是用异步的方式来实现的,如 Kafka