• 观察者模式


    观察者模式的定义:

    定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。

    观察者模式的类图:

    根据类图自定义主题、观察者接口:

    package observer;
    
    /**
     * 主题接口
     * @author 发挥哥
     *
     */
    public interface Subject {
    	/**
    	 * 注册观察者
    	 * @param observer 观察者对象
    	 */
    	void registerObserver(Observer observer);
    	/**
    	 * 移除观察者
    	 * @param observer 观察者对象
    	 */
    	void removeObserver(Observer observer);
    	/**
    	 * 通知所有观察者
    	 */
    	void notifyObservers();
    }
    
    package observer;
    
    /**
     * 观察者接口
     * @author 发挥哥
     *
     */
    public interface Observer {
    	/**
    	 * 主题通知所有观察者,执行该方法
    	 * @param msg
    	 */
    	void update(String msg);
    }
    

    创建主题类和观察者类:

    package observer;
    
    import java.util.ArrayList;
    import java.util.List;
    import observer.Subject;
    
    /**
     * 自定义主题类,实现自定义主题接口
     * @author 发挥哥
     *
     */
    public class TestSubject implements Subject {
    	/**
    	 * 所有注册到该主题的观察者
    	 */
    	private List<Observer> observers=new ArrayList<>();
    	/**
    	 * 主题的状态
    	 */
    	private String msg;
    
    	@Override
    	public void registerObserver(Observer observer) {
    		// TODO Auto-generated method stub
    		if(observers!=null&&observer!=null)
    			observers.add(observer);
    	}
    
    	@Override
    	public void removeObserver(Observer observer) {
    		// TODO Auto-generated method stub
    		if(observers.contains(observer)) {
    			observers.remove(observer);
    		}
    	}
    
    	@Override
    	public void notifyObservers() {
    		// TODO Auto-generated method stub
    		observers.stream().forEach(observer->{
    			observer.update("主题更新了,大家注意:"+this.msg);
    		});
    	}
    	
    	/**
    	 * 编辑主题的状态后,通知所有观察者
    	 * @param msg
    	 */
    	public void editMsg(String msg) {
    		this.msg = msg;
    		notifyObservers();
    	}
    }
    
    package observer;
    
    /**
     * 观察者1
     * @author 发挥哥
     *
     */
    public class Test1Observer implements Observer {
    	/**
    	 * 创建观察者对象时,将其注册到指定的主题
    	 * @param subject
    	 */
    	public Test1Observer(Subject subject) {
    		subject.registerObserver(this);
    	}
    
    	/**
    	 * 当主题状态变化时,通过调用该方法通知观察者
    	 */
    	@Override
    	public void update(String msg) {
    		// TODO Auto-generated method stub
    		System.out.println(this.getClass().getName()+"收到主题发来的消息:"+msg);
    	}
    
    }
    
    package observer;
    
    /**
     * 观察者2(仅为测试,实现与观察者1一样)
     * @author 发挥哥
     *
     */
    public class Test2Observer implements Observer {
    	
    	public Test2Observer(Subject subject) {
    		subject.registerObserver(this);
    	}
    
    	@Override
    	public void update(String msg) {
    		// TODO Auto-generated method stub
    		System.out.println(this.getClass().getName()+"收到主题发来的消息:"+msg);
    	}
    
    }
    

    创建测试方法:

    package observer;
    
    public class TestMain {
    
    	public static void main(String[] args) {
    		//创建主题
    		TestSubject subject=new TestSubject();
    		//创建观察者1并注册到主题
    		Observer observer1=new Test1Observer(subject);
    		//创建观察者2并注册到主题
    		Observer observer2=new Test2Observer(subject);
    		//主题进行2次状态修改
    		subject.editMsg("第一次调整!");
    		subject.editMsg("第二次调整!");
    	}
    }
    

    测试结果如下:

    observer.Test1Observer收到主题发来的消息:主题更新了,大家注意:第一次调整!
    observer.Test2Observer收到主题发来的消息:主题更新了,大家注意:第一次调整!
    observer.Test1Observer收到主题发来的消息:主题更新了,大家注意:第二次调整!
    observer.Test2Observer收到主题发来的消息:主题更新了,大家注意:第二次调整!

    以上是自定义主题和观察者接口及其实现,JDK中已经帮我们实现了观察者模式,借助于java.util.Observable和java.util.Observer:

    package java.util;
    
    public class Observable {
        private boolean changed = false;
        private Vector<Observer> obs;
    
        public Observable() {
            obs = new Vector<>();
        }
    
        public synchronized void addObserver(Observer o) {
            if (o == null)
                throw new NullPointerException();
            if (!obs.contains(o)) {
                obs.addElement(o);
            }
        }
    
        public synchronized void deleteObserver(Observer o) {
            obs.removeElement(o);
        }
    
        public void notifyObservers() {
            notifyObservers(null);
        }
    
        public void notifyObservers(Object arg) {
            Object[] arrLocal;
    
            synchronized (this) {
                if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
    
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(this, arg);
        }
    
        public synchronized void deleteObservers() {
            obs.removeAllElements();
        }
    
        protected synchronized void setChanged() {
            changed = true;
        }
    
        protected synchronized void clearChanged() {
            changed = false;
        }
    
        public synchronized boolean hasChanged() {
            return changed;
        }
    
        public synchronized int countObservers() {
            return obs.size();
        }
    }
    
    package java.util;
    
    public interface Observer {
        void update(Observable o, Object arg);
    }
    

    类java.util.Observable和接口java.util.Observer比较简单,直接借此创建主题和观察者类:

    package observer.jdk;
    
    import java.util.Observable;
    /**
     * 主题1
     * @author 发挥哥
     *
     */
    public class MySubject extends Observable {
    	private String msg;
    
    	/**
    	 * 编辑主题消息,并将变动标志置true,通知所有观察者,再将标志置false
    	 * @param msg
    	 */
    	public void editMsg(String msg) {
    		if(msg==null) {
    			this.msg = msg;
    			setChanged();
    			notifyObservers("主题将消息置空!");
    			clearChanged();
    			return;
    		}
    		if(this.msg!=null&&msg!=null&&this.msg.equals(msg)) {
    			return;
    		} 
    		
    		this.msg = msg;
    		setChanged();
    		notifyObservers("主题更新消息:"+this.msg);
    		clearChanged();		
    	}
    }
    
    package observer.jdk;
    
    import java.util.Observable;
    /**
     * 主题2(仅为测试,与观察者1实现一样)
     * @author 发挥哥
     *
     */
    public class OtherSubject extends Observable {
    	private String msg;
    
    	public void editMsg(String msg) {
    		if(msg==null) {
    			this.msg = msg;
    			setChanged();
    			notifyObservers("主题将消息置空!");
    			clearChanged();
    			return;
    		}
    		if(this.msg!=null&&msg!=null&&this.msg.equals(msg)) {
    			return;
    		} 
    		
    		this.msg = msg;
    		setChanged();
    		notifyObservers("主题更新消息:"+this.msg);
    		clearChanged();		
    	}
    }
    
    package observer.jdk;
    
    import java.util.Observable;
    import java.util.Observer;
    /**
     * 观察者
     * @author 发挥哥
     *
     */
    public class MyObserver implements Observer {
    	/**
    	 * 通过该方法将观察者自己注册到主题
    	 * @param observable
    	 */
    	public void registerToSubject(Observable observable) {
    		observable.addObserver(this);
    	}
    
    	@Override
    	public void update(Observable o, Object arg) {
    		System.out.println(this.getClass().getName()+"收到主题"+o.getClass().getName()+"更新消息:"+arg);
    	}
    }
    

    编写测试方法:

    package observer.jdk;
    
    public class MyTest {
    	public static void main(String[] args) {
    		//创建主题1、主题2
    		MySubject subject1=new MySubject();
    		OtherSubject subject2=new OtherSubject();
    		//创建观察者
    		MyObserver observer=new MyObserver();
    		//将观察者注册到主题1、主题2
    		observer.registerToSubject(subject1);
    		observer.registerToSubject(subject2);
    		//主题1、主题2分别更新状态
    		subject1.editMsg("主题一更新");
    		subject2.editMsg("主题二更新");
    	}
    }
    

    测试结果如下:

    observer.jdk.MyObserver收到主题observer.jdk.MySubject更新消息:主题更新消息:主题一更新
    observer.jdk.MyObserver收到主题observer.jdk.OtherSubject更新消息:主题更新消息:主题二更新

    比较好的观察者模式应用是微信公众号,公众号就是我们的主题,粉丝就是观察者。功能如下:

    • 1、公众号就是主题,业务就是推送新消息;
    • 2、观察者订阅主题,有新的消息自动送来;
    • 3、当不想要此主题消息时,取消订阅即可;

    总结:

    观察者模式的实现方式是在观察者调用主题的addObserver方法将自己添加到主题持有的观察者列表里,主题在需要的时候调用notifyObservers遍历观察者列表,并分别调用观察者的update方法。

    打个比方,主题就是婚介所,多个观察者就是多个未婚大龄男青年,男青年注册会员之后将自己的联系方式交给婚介所,婚介所在合适的时候(比如来了适龄女青年),就会挨个给注册的男青年打电话通知。

  • 相关阅读:
    Content-Type 之 application/json 与 text/javascript
    利用 filter 机制 给 静态资源 url 加上时间戳,来防止js和css文件的缓存,利于开发调试
    Tomcat 启动报错:No default web.xml
    $.parseJson 在 firefox 下返回 null 的问题
    利用 spring bean 的属性 init-method 解决因为数据库连接没有初始化而导致首次点击页面超慢的问题
    spring项目的 context root 修改之后,导致 WebApplicationContext 初始化两次的解决方法
    proxool 连接池警告分析:appears to have started a thread named [HouseKeeper] but has failed to stop it
    Log4j 输出的日志中时间比系统时间少了8小时的解决方法,log4j日志文件重复输出
    itext 实现pdf打印数字上标和下标
    log4j 实现只输入我们指定包的日志
  • 原文地址:https://www.cnblogs.com/kibana/p/8808388.html
Copyright © 2020-2023  润新知