观察者模式(Observer Pattern)

一、模式动机

观察者模式用于描述对象之间的依赖关系,它引入了观察者和观察目标两类不同的角色,由于提供了抽象层,它使得增加新的观察者和观察目标都很方便。观察者模式广泛应用于各种编程语言的事件处理模型中,Java语言也提供了对观察者模式的全面支持。

  • 一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动
  • 定义了对象之间一种一对多的依赖关系,让一个对象的改变能够影响其他对象
  • 发生改变的对象称为观察目标,被通知的对象称为观察者
  • 一个观察目标可以对应多个观察者

二、模式定义

  • 观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新
  • 观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式
  • 观察者模式是一种对象行为型模式

三、模式结构

抽象目标类

public abstract class Subject {
protected ArrayList observers<Observer> = new ArrayList();

public void attach(Observer observer) {
        observers.add(observer);    
}
public void detach(Observer observer) {
        observers.remove(observer);    
}
public abstract void notify( );
}

具体目标类

public class ConcreteSubject extends Subject {
    //实现通知方法
    public void notify() {
        for(Object obs:observers) {
            ((Observer)obs).update();
        }
    }	
}

抽象观察者类

public interface Observer {
    public void update();
}

具体观察者类

public class ConcreteObserver implements Observer {

    public void update() {
        ……
    }
}

四、案例实现

案例背景

股票

案例结构

代码实现

抽象目标类:股票

public interface Stocks {

    ArrayList<Observer> OBSERVERS = new ArrayList<>();
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyInvestor();

}

目标类:股票

public class Stock implements Stocks{

    private String stockName;
    private int price;

    public Stock(String stockName, int price) {
        this.stockName = stockName;
        this.price = price;
    }

    public Stock() {

    }

    public String getStockName() {
        return stockName;
    }

    public void setStockName(String stockName) {
        this.stockName = stockName;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public void attach(Observer observer) {
        OBSERVERS.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        OBSERVERS.remove(observer);
    }

    @Override
    public void notifyInvestor() {
        if(price >= 25*1.05 || price <= 25*0.95){
            System.out.println("通知:股票价格变动幅度超过5%!");
            for (Observer observer: OBSERVERS) {
                observer.upDate();
            }
        } else {
            System.out.println("股票价格变化幅度没有超过5%!!");
        }
    }
}

抽象观察者类

public interface Observer {

    void upDate();

}

具体观察者类A

public class ConcreteObserverA implements Observer {

    private String name;
    private Stock stock;

    public ConcreteObserverA(String name, Stock stock) {
        this.name = name;
        this.stock = stock;
    }

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

    @Override
    public void upDate() {
        System.out.println("通知"+name+":"+stock.getStockName()+"的股票价格变化幅度超过5%,股票价格为:"+stock.getPrice());
    }
}

具体观察者类B

public class ConcreteObserverB implements Observer {

    private String name;

    public ConcreteObserverB(String name, Stock stock) {
        this.name = name;
        this.stock = stock;
    }

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

    private Stock stock;

    @Override
    public void upDate() {
        System.out.println("通知"+name+":"+stock.getStockName()+"的股票价格变化幅度超过5%,股票价格为:"+stock.getPrice());
    }
}

具体观察者类C

public class ConcreteObserverC implements Observer {

    private String name;
    private Stock stock;

    public ConcreteObserverC(String name, Stock stock) {
        this.name = name;
        this.stock = stock;
    }

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

    @Override
    public void upDate() {
        System.out.println("通知"+name+":"+stock.getStockName()+"的股票价格变化幅度超过5%,股票价格为:"+stock.getPrice());
    }
}

测试类

public class Test {

    public static void main(String[] args) {
        System.out.println("设计模式,2020006924,于鑫");
        Stock stock1 = new Stock("股票1",25);
        System.out.println(stock1.getStockName()+"的股价为:"+stock1.getPrice());
        Observer obs1,obs2,obs3;
        obs1 = new ConcreteObserverA("smith",stock1);
        obs2 = new ConcreteObserverB("tom",stock1);
        obs3 = new ConcreteObserverC("李白",stock1);
        stock1.attach(obs1);
        stock1.attach(obs2);
        stock1.attach(obs3);
        stock1.setPrice(22);

        stock1.notifyInvestor();

    }

}

案例结果

五、模式分析

  • 有时候在具体观察者类ConcreteObserver中需要使用到具体目标类ConcreteSubject中的状态(属性),会存在关联或依赖关系
  • 如果在具体层之间具有关联关系,系统的扩展性将受到一定的影响,增加新的具体目标类有时候需要修改原有观察者的代码,在一定程度上违背了开闭原则,但是如果原有观察者类无须关联新增的具体目标,则系统扩展性不受影响

六、总结

模式优点

  • 可以实现表示层和数据逻辑层的分离
  • 在观察目标和观察者之间建立一个抽象的耦合
  • 支持广播通信,简化了一对多系统设计的难度
  • 符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便

模式缺点

  • 将所有的观察者都通知到会花费很多时间
  • 如果存在循环依赖时可能导致系统崩溃
  • 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化

使用情形

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用
  • 一个对象的改变将导致一个或多个其他对象发生改变,且并不知道具体有多少对象将发生改变,也不知道这些对象是谁
  • 需要在系统中创建一个触发链

热门相关:有个人爱你很久   名门天后:重生国民千金   豪门重生盛世闲女   重生当学神,又又又考第一了!   豪门重生盛世闲女