Java 设计模式 观察者模式

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

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

在一个系统中,实现这种一对多的并且之间有必定关联的逻辑的时候,因为须要保持他们之间的协同关系,因此最简便的方法是采用紧耦合,把这些对象绑定到一块儿。可是这样一来,一旦有扩展或者修改的时候,开发人员所面对的难度很是大,并且很容易形成Bug。那么观察者模式就解决了这么一个问题,在保持一系列观察者和被观察者对象协同工做的同时,把解耦了它们函数

Coding

抽象观察者角色类this

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);
    }
}

抽象主题角色类code

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){};
}

具体主题角色类server

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去回收的时候,只会处理没有被引用的对象,只要是还被引用的对象时不会被回收掉的。因此若是在长期不关闭的系统中(好比监控系统),大量的代码使用+=而不-=,运行时间长之后有可能会内存溢出

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

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