观察者模式的Java实现

观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

介绍

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决:使用面向对象技术,可以将这种依赖关系弱化。

关键代码:在抽象类里有一个 ArrayList 存放观察者们。

核心:观察者模式主要用于1:N的通知,当一个对象(目标对象Subject或Objservable--消息发布)的状态变化时,它需要及时告知一系列对象(观察者对象,Observer--消息订阅),令他们做出响应

通知观察者方式:

  • 推:每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收
  • 拉:观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。

应用实例: 

  • 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价
  • 西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作

优点: 

  • 观察者和被观察者是抽象耦合的
  • 建立一套触发机制

缺点:

  •  如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

使用场景:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

实际场景:

  • 聊天室程序的创建。服务器创建好之后,A、B、C三个客户端连上服务器进行群聊,A向服务器发送数据,服务器端聊天数据改变,于是将改变的数据分发到BC客户端,也就是说每个客户端需要更新服务器端的数据。
  • 网站上,很多人订阅了Stack Overflow上的C++技术栈主题,当这个技术栈主题的更新新的文章的时就会向订阅该主题的用户推送新的文章。
  • 统一服务器大区进行lol匹配或排位时,服务器需要实时的将每个人的位置信息发送到同一局的所有客户端上。

开发中常见的场景:

  • Servlet中,监听器的实现
  • Android中的广播机制
  • JDK的AWT中事件处理模型,基于观察者模式的委派事件模式(Delegation Event Model)事件源-目标对象 事件监听器-观察者
  • 京东商城中,群发某商品打折信息

注意事项:

  • JAVA 中已经有了对观察者模式的支持类
  • 避免循环引用
  • 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式

实现

步骤1:创建目标类,实现一些目标类需要的共有函数 

package com.ly.observer;

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

/**
 * 目标类,把一些共有操作放在这里,对接具体
 * 相关观察者的操作放在它所对应的具体目标类中
 * @author Young 2020-09-06
 */

public class Subject {
    private List<Observer> list = new ArrayList<>();
    
    public void register(Observer observer) {
        list.add(observer);
    }
    
    public void unregister(Observer observer) {
        list.remove(observer);
    }
    
    public void notifyAllObserver() {
        for (Observer observer: list) {
            observer.update(this);
        }
    }
}

步骤2:创建更为具体的目标类,当其状态改变时,通知对应的ObserverA类对象

package com.ly.observer;

/**
 * 具体的目标类,可以根据观察者需要增加的信息进行补充
 * 甚至重写NotifyAllObserver()
 * @author Young 2020-09-06
 */

public class ConcreteSubjectA extends Subject {
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        this.notifyAllObserver();
    }
}

步骤3:创建抽象的观察者,声明update函数接口

package com.ly.observer;

/**
 * 抽象的观察者
 * @author Young 2020-09-06
 */

public interface Observer {
    void update(Subject subject);
}

步骤4:创建某一类具体的观察者

package com.ly.observer;

/**
 * 具体的某一类观察者
 * @author Young 2020-09-06
 */
public class ObserverA implements Observer {

    private int aState;
    
    @Override
    public void update(Subject subject) {
        aState = ((ConcreteSubjectA) subject).getState();
    }

    public int getState() {
        return aState;
    }

    public void setState(int aState) {
        this.aState = aState;
    }
}

步骤5:客户端测试

package com.ly.observer;

/**
 * 客户端测试
 * @author Young 2020-09-06
 */

public class Client {
    public static void main(String[] args) {
        ConcreteSubjectA subject = new ConcreteSubjectA();
        
        ObserverA observerA1 = new ObserverA();
        ObserverA observerA2 = new ObserverA();
        ObserverA observerA3 = new ObserverA();
        
        subject.register(observerA1);
        subject.register(observerA2);
        subject.register(observerA3);
        
        subject.setState(100);
        System.out.println(observerA1.getState());
        System.out.println(observerA2.getState());
        System.out.println(observerA3.getState());
        
        System.out.println();
        subject.setState(200);
        System.out.println(observerA1.getState());
        System.out.println(observerA2.getState());
        System.out.println(observerA3.getState());
    }
}

结果:

100
100
100

200
200
200

使用jdk8自带的观察者模式

Java SE提供java.util.Observable和java.util.Observer来实现观察者模式,据说后续版本被废弃(JDK 8,尚可使用)

package com.ly.observerjdk;

import java.util.Observable;

/**
 * 使用jdk中自带的观察者相关类
 * Observable对应于前文的Subject
 * @author Young 2020-09-06
 */

public class ConcreteSubjectA extends Observable {

    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        setChanged();
        notifyObservers(state);
    }
}
package com.ly.observerjdk;

import java.util.Observable;
import java.util.Observer;

/**
 * 使用jdk中自带的观察者相关类
 * Observer对应于前文抽象的Observer
 * @author Young 2020-09-06
 */

public class ObserverA implements Observer{

    private int aState;
    
    @Override
    public void update(Observable o, Object arg) {
        aState = ((ConcreteSubject) o).getState();
    }

    public int getState() {
        return aState;
    }

    public void setState(int aState) {
        this.aState = aState;
    }
}
package com.ly.observer;

/**
 * 客户端测试
 * @author Young 2020-09-06
 */

public class Client {
    public static void main(String[] args) {
        ConcreteSubjectA subject = new ConcreteSubjectA();
        
        ObserverA observerA1 = new ObserverA();
        ObserverA observerA2 = new ObserverA();
        ObserverA observerA3 = new ObserverA();
        
        subject.register(observerA1);
        subject.register(observerA2);
        subject.register(observerA3);
        
        subject.setState(100);
        System.out.println(observerA1.getState());
        System.out.println(observerA2.getState());
        System.out.println(observerA3.getState());
        
        System.out.println();
        subject.setState(200);
        System.out.println(observerA1.getState());
        System.out.println(observerA2.getState());
        System.out.println(observerA3.getState());
    }
}

结果:

1000
1000
1000

2000
2000
2000

参考资料:

https://www.runoob.com/design-pattern/observer-pattern.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值