• Java设计模式:观察者模式


    本文是自己的学习笔记,主要参考以下资料

    - 大话设计模式,程杰著,清华大学出版社出版
    - 马士兵教育


    1、原始的观察者代码

    设想一个场景,用代码模拟照看小孩,当小孩哭的时候就安慰小孩。

    class Child {
    	private boolean cry = false;
    	public boolean isCrying() {
    		return cry;
    	}
    	public void wakeUp() {
    		cry = true;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    最直接的做法就是while循环,直到某个变量发生变化。

    public Child child;
    public static void main(String[] args) {
    	while(true) {
    		if(child.isCrying()) {
    			//do something
    		}
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当其它的线程调用wakeUp()方法时,程序自然就监控到孩子正在哭然后做对应的操作。


    2、被观察者通知观察者

    但是这样不断地while循环很占资源。我们换一个版本的代码,在其中加入观察者。并且不是观察者主动观察被监视对象,而是被监视对象发生动作时主动通知观察者。

    class Dad { 
    	public void feed(){
    		//feed
    	}
    }	
    class Child {
    	private Dad dad = new Dad();
    	private boolean cry = false;
    	public boolean isCrying() {
    		return cry;
    	}
    	public void wakeUp() {
    		cry = true;
    		dad.feed();
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这样就不用while循环监视孩子是否在哭,将处理逻辑和wakeUp()方法强绑定,只要调用wakeUp()方法,自然就会调用对应的事件处理逻辑。

    但这样的弊端也显而易见,一个被观察对象可以用多个观察者,当我们想要多加入一个观察者时就显得很费劲。比如再加入观察者Mon和Dog。

    class Dad { public void feed(){//feed}}	
    class Mon { public void hug(){//hug}}	
    class Dog { public void bark(){//bark}}	
    class Child {
    	private Dad dad = new Dad();
    	private Mon mon = new Mon();
    	private Dog dog = new Dog();
    
    	private boolean cry = false;
    	public boolean isCrying() {
    		return cry;
    	}
    	public void wakeUp() {
    		cry = true;
    		dad.feed();
    		mon.hug();
    		dog.bark();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19



    3、观察者处理逻辑与被观察者解耦


    我们不想让观察者的观察到事件后的处理逻辑和被观察者的行为强耦合,办法也很简单,和大多数设计模式一样。我们定义接口抽象观察者行为。被观察者维护一个集合,集合里存储观察着它的对象。当被观察者发生特定事件时,遍历通知集合里的观察者。

    观察者的代码

    interface Observer {	
    	void actionOnWakeUp();
    }
    class Dad implements Observer {
    	void actionOnWakeUp(){System.out.println("feeding...");}
    }
    class Mon implements Observer {
    	void actionOnWakeUp(){System.out.println("hugging...");}
    }
    class Dog implements Observer {
    	void actionOnWakeUp(){System.out.println("barking...");}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    被观察者

    class Child{
    	private List<Observer> observerList = new ArrayList<>();
    	// 省略observerList的add, delete等方法
    	private boolean cry = false;
    	public void wakeUp() {
    		cry = true;
    		for(Observer observer: observerList) {
    			observer.actionOnWakeUp();
    		}
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这样就能实现观察者和被观察对象的行为解耦。




    但这样还不完美,因为观察者应该要根据不同的情况对被观察者的动作作出反应。比如孩子哭了是在什么时候哭,是刚刚吃完饭哭,还是半夜哭,还是自己玩的时候跌倒了哭。不同的情况应该有不同的处理逻辑。

    要想知道具体情况,那就是在方法里传参。我们需要定义一个事件类,里面描述我们需要的一切事件元信息。

    代码如下,省略了基本重复的部分。

    interface Observer {	
    	void actionOnWakeUp(Event event);
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    Kotlin Sequence 是时候派上用场了
    centOS7| 编译安装 gdal 库
    无涯教程-JavaScript - INFO函数
    ts的文字类型
    如何更精准定位你的Facebook广告受众?
    前端框架 网络请求 Fetch Axios
    Vue3源码reactive和readonly对象嵌套转换,及实现shallowReadonly
    如何制作一个体温收集表
    No theme registered! Use AbpThemingOptions to register themes
    BGP高级特性——ORF、GTSM、4字节AS号
  • 原文地址:https://blog.csdn.net/sinat_38393872/article/details/125416499