Java 设计模式 观察者模式

每个角色都对应这一个类,比如观察者模式,观察者对应着观察者类,被观察者对应着被观察者类。实际上,设计模式就是通过面向对象的特性,将这些角色解耦

观察者模式本质上就是一种订阅 / 发布的模型,从逻辑上来说就是一对多的依赖关系。什么意思呢?好比是一群守卫盯着一个囚犯,只要囚犯一有异动,守卫就必须马上采取行动(也有可能是更新状态,本质上也是一种行动),那么守卫就是观察者,囚犯就是被观察者

在一个系统中,实现这种一对多的而且之间有一定关联的逻辑的时候,由于需要保持他们之间的协同关系,所以最简便的方法是采用紧耦合,把这些对象绑定到一起。但是这样一来,一旦有扩展或者修改的时候,开发人员所面对的难度非常大,而且很容易造成Bug。那么观察者模式就解决了这么一个问题,在保持一系列观察者和被观察者对象协同工作的同时,把解耦了它们

Coding

抽象观察者角色类

public interface Observer 
{
    // 更新接口
    public void update();
}

具体观察者角色类

public class ConcreteObserver implements Observer 
{
    // 观察者的状态
    private String observerState = "Initial";     // 观察者初始状态,会随着被观察者变化而变化
    private String name;                         // 观察者名称,用于标记不同观察者
    private Subject concreteSubject;
    
    // 构造观察者,并传入被主题对象,以及标识该观察者名称
    public ConcreteObserver(Subject concreteSubject, String name)
    {
        this.concreteSubject = concreteSubject;
        this.name = name;
        System.out.println("我是观察者" + name +", 我的状态是" + observerState);
    }
    
    // 观察者状态随主题主题改变
    public void update() 
    {
        observerState = concreteSubject.SubjectState;
        System.out.println("我是观察者" + name +", 我的状态是" + observerState);
    }
}

抽象主题角色类

import java.util.List;

public abstract class Subject 
{
    // 用来保存注册的观察者对象
    List<Observer> list = null;
    String SubjectState;
    
    // 注册观察者对象
    public void attach(Observer observer){};
    
    //删除观察者对象
    public void detach(Observer observer){};
    
    // 通知所有注册的观察者对象
    public void nodifyObservers(String newState){};
}

具体主题角色类

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

public class ConcreteSubject extends Subject
{
    private List<Observer> list = new ArrayList<Observer>();
    public String SubjectState;


    // 注册观察者对象
    public void attach(Observer observer)
    {
        list.add(observer);
        System.out.println("Attached an observer");
    }
    
    //删除观察者对象
    public void detach(Observer observer){
        
        list.remove(observer);
    }
    
    // 通知所有注册的观察者对象
    public void nodifyObservers(String newState)
    {
        for(Observer observer : list)
        {
            observer.update();
        }
    }
}

客户端

public class Client 
{
    public static void main(String[] args) 
    {
        // 创建主题对象
        Subject concreteSubject = new ConcreteSubject();
        concreteSubject.attach(new ConcreteObserver(concreteSubject, "安倍晴明"));
        concreteSubject.attach(new ConcreteObserver(concreteSubject, "神乐"));
        concreteSubject.attach(new ConcreteObserver(concreteSubject, "源博雅"));
        
        concreteSubject.SubjectState = "结界突破!";
        concreteSubject.nodifyObservers(concreteSubject.SubjectState);
    }
}

运行结果

我是观察者安倍晴明, 我的状态是Initial
Attached an observer
我是观察者神乐, 我的状态是Initial
Attached an observer
我是观察者源博雅, 我的状态是Initial
Attached an observer
我是观察者安倍晴明, 我的状态是结界突破!
我是观察者神乐, 我的状态是结界突破!
我是观察者源博雅, 我的状态是结界突破!

观察者模式关键点

在主题(被观察者)中,定义了一个集合用来存放观察者,编写了注册attach()和移除detach()观察者的方法,这体现了一对多的关系,也提供了可以控制观察者的方式
关键点1:每个观察者需要被保存到主题(被观察者)的集合中,并且被观察者提供添加和删除的方式

观察者和被观察者之间的交互活动。在添加一个观察者时,把被主题(被观察者)对象以构造函数的形式给传入了观察者。最后主题(被观察者)执行nodifyObservers()方法,触发所有观察者的update()方法以更新状态
关键点2:被主题(被观察者)把自己传给观察者,当状态改变后,通过遍历或循环的方式逐个通知列表中的观察者

但这里有个问题,主题(被观察者)是通过构造函数参数的形式,传给观察者的,而观察者对象时被attach()到主题(被观察者)的list中
关键点3:虽然解耦了观察者和主题(被观察者)的依赖,让各自的变化不大影响另一方的变化,但是这种解耦并不彻底,没有完全解除两者之间的耦合

关键点4:在事件中,订阅者和发布者之间是通过把事件处理程序绑定到委托,并不是把自身传给对方。所以解决了观察者模式中不完全解耦的问题

委托,事件,和观察者模式之间的关系

观察者模式,必然涉及到2委托和事件这两种类型

委托

委托就是可把方法当做另一个方法参数来传递,需要注意方法签名。委托可以看做是方法的抽象,也就是方法的“类”,一个委托的实例可以是一个或者多个方法。我们可以通过+=或者-=把方法绑定到委托或者从委托移除

事件

事件是一种特殊的委托。首先事件也是委托,只是在声明事件的时候,需要加上event,如果你用reflector去看一个事件,你会发现里面就3样东西,一个Add_xxxx方法,一个Remove_xxx方法,一个委托。和上面所定义主题(被观察者)时的注册attach()和移除detach()有些联系

.Net事件机制

实际上.Net的事件机制就是观察者模式的一种体现,并且是利用委托来实现。本质上事件就是一种订阅-发布模型也就是观察者模式,这种机制中包含2个角色,一个是发布者,一个是订阅者。发布者类也就类似于主题(被观察者),发布者类包含事件和委托定义,以及其之间的关系,发布者类的对象调用事件通知其他订阅者。而订阅者类也就类似于观察者,观察者接受事件,并且提供处理的逻辑。也就是说,订阅者对象(观察者)中的方法会绑定到发布者(被观察者)对象的委托中,一旦发布者(被观察者)中事件被调用,发布者(被观察者)就会调用委托中绑定的订阅者(观察者)的处理逻辑或者说是处理程序,这就是通过观察者模式实现的事件

观察者模式问答

  • 在普通的观察者模式中,解耦并不彻底,那么在事件的发布订阅模型中,解耦彻底吗?为什么?

答案是肯定的。因为在事件中,订阅者和发布者之间是通过把事件处理程序绑定到委托,并不是把自身传给对方。所以解决了观察者模式中不完全解耦的问题

  • 通过委托绑定方法来实现观察者模式,会不会有什么隐患?

有的,通过+=去把方法绑定到委托,很容易忘记-=。如果只绑定不移除,这个方法会一直被引用。我们知道GC去回收的时候,只会处理没有被引用的对象,只要是还被引用的对象时不会被回收掉的。所以如果在长期不关闭的系统中(比如监控系统),大量的代码使用+=而不-=,运行时间长以后有可能会内存溢出

  • 事件,委托,观察者模式之间的关系

委托是一种类型,事件是一种特殊的委托,观察者模式是一种设计模式,事件的机制是观察者模式的一种实现,其中订阅者和发布者通过委托实现协同工作

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000008152059
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞