观察者模式

Posted zhuchangwu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了观察者模式相关的知识,希望对你有一定的参考价值。

在处理类之间的多对一的依赖关系时,观察者设计模式应运而生了,它的出现实现了代码的瘦身,类之间的解耦,本文分三部分:

  • 非观察者模式的多对一的依赖处理
  • 观察者模式对多对一依赖处理的优化
  • Java内置的观察者

假设场景: 前端用户向后端服务器发送不同的请求,后端的Selector区的分不同的请求,回调不同的Handler处理请求, 下面的示例代码

非观察者

当Selector发现数据改变时,想要回调Handler的方法的前提是它要维护一个Handler的引用, 我们在构造函数中传递进去

@Data
public class Selector 
    private String username; 
    private String password; 

    private Handler1 currentConditions;

    public Selector(Handler1 currentConditions) 
        this.currentConditions = new Handler1();
    

    public void datachange() 
        currentConditions.update(getUsername(),getPassword());
    

    // 添加假数据
    public void setData(String username,String password)
        this.username=username;
        this.password=password;
        datachange();
    

Handkler 的成员变量和Selector一样, 同时真正干活的人是handler, 它里面只有干活的方法以及承载数据的字段, 干活的方法会被回调, 字段由Selector负责初始化

public class Handler1 
    private String  username;
    private String password;

    public void update( String  username, String  password)
        this.username=username;
        this.password=password;
        display();
    

    public void display()
        System.out.println("Handler1 : username"+ username);
        System.out.println("Handler1 : password"+username);
    

测试:

public class text 
    public static void main(String[] args) 
        Handler1 currentConditions = new Handler1();
        Selector weatherData = new Selector(currentConditions);
        weatherData.setData("张三","123");
    

上面的设计方法实现了,当Selector发现新的数据改变时,回调指定的handler的需求,但是缺点很快就暴露出来, Handler引用以构造方法的形式在Selector维护,如果有100个handler, 是不是意味着Selector的构造函数重载100份?


观察者模式

观察者解决了上面的问题,在观察者模式中有两个角色的划分,1.SubJect 2.Observer, 其中 SubJect负责回调Observer提供的方法处理数据, Observer就是观察者,伺机而动, 它之所以能够实现两者的解耦,实现代码的瘦身,精华就是在Subject的构造函数不再维护单一的某一个Observer的引用,而是一个泛型是为Observer类型的集合, 为了防止把非观察者的对象添加到集合中, 故 在原有的基础上,向上进行了一层抽象,把Subject和Observer设计成接口,Subject提供了注册/移除/通知观察者的方法,通知哪个方法呢? 于是Observer接口中提供了需要被回调的抽象方法, 做完这层封装之后,就有了这个观察者体系, 所有的观察者必须实现Observer接口,重写它的方法,任何回调观察者的类,也必须实现Subject,重写它的方法

实例代码:

public interface Subject 
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObserver();


public interface Observer 
    public void update(String m,String p);

Subject的实现类:

@Data
public class Selector implements Subject 
    private String username;
    private String password;

    private ArrayList<Observer> list;

    public Selector() 
      this.list = new ArrayList<Observer>();
    

    // 添加假数据
    public void setData( String username, String password)
        this.username=username;
        this.password=password;

    

    @Override
    public void registerObserver(Observer o) 
        list.add(o);
    

    @Override
    public void removeObserver(Observer o) 
        list.remove(o);
    

    @Override
    public void notifyObserver() 
        list.forEach(s->s.update(getPassword(),getUsername()));
    

Observer的实现:

public class Handler1 implements Observer 
    private String username;
    private String password;

    @Override
    public void update( String username,String password)
        this.username=username;
        this.password=password;
        display();
    

    public void display()
        System.out.println("  Handler1    "+ username);
        System.out.println("  Handler1    "+password);
    

测试:

public class text 
    public static void main(String[] args) 
        Handler1 secondConditions = new Handler1();
        Selector weatherData = new Selector();
        // 注册观察者
        weatherData.registerObserver(secondConditions);
        weatherData.setData("张三","123");
        weatherData.notifyObserver();
    

我们自己实现的观察者函数回调的特性: : 虽然实现我们一开始的需求,但是可以看到,数据是在Subject端进行初始化的, 并没有传递到观察端,由观察者自主分配数据


java内置的观察者

java内置的观察中同样分为两种角色,1.Obserable 2.Observer 其中Observer是观察者,同样不变的是,他是个接口

但是: Obserable是一个抽象类, java为我们实现了 注册/移除,通知的方法, 在不允许多继承的java中,抽象类是有缺点的,这使得当前类不能再继承其他类

此外: 相对于我们自定义的观察者, 它做到了把数据传递到 观察者客户端,任由观察者自主的分配这些数据

如何使用:
对于Obserable来说, 继承他

  • 由于java实现了主要的方法, 我们只关注如何使用这几个方法就行了
  • 1: 在回调之前 , 必须先使用 this.setChanged(); 否则信息不会通知到观察者
  • 2: 通知的方法this.notifyObservers(Object obj));
@Data
public class Selector extends Observable 
    private String username;
    private String  password;

    // 添加假数据
    public void setData( String username, String password) 
        this.username = username;
        this.password = password;
        datachange();
    

    // 当信息更改, 就推送
    public void datachange() 
        // todo 加上这一条,当有信息改变时, 就执行
        this.setChanged();
        // 通知时,把信息同步推送给观察者
       this.notifyObservers(new Data(getUsername(),getPassword()));
    

    @lombok.Data
    public class Data 
        private String username;
        private String  password;

        public Data( String username, String password) 
            this.username = username;
            this.password = password;
        
    

观察者: 继承Observer, 重写它被回调的方法

public class Handler1 implements Observer 
    private String username; 
    private String password;  

    @Override
    public void update(Observable observable, Object arg0) 
        this.password = ((Selector.Data) (arg0)).getPassword();
        this.username = ((Selector.Data) (arg0)).getUsername();
        display();
    

    public void display() 
        System.out.println("Handler1 :    " + username);
        System.out.println("Handler1 : " + password);
    

测试

public class text 
    public static void main(String[] args) 
        Handler1 handler1 = new Handler1();
        Selector weatherData = new Selector();
        // 先注册的观察者,后执行
        weatherData.addObserver(handler1);
        weatherData.setData("张三","123");
    

以上是关于观察者模式的主要内容,如果未能解决你的问题,请参考以下文章

观察者模式是啥

观察者模式

设计模式之观察者模式

Android开发模式之观察者模式

[设计模式]观察者模式与订阅模式

设计模式-行为型模式-观察者模式