zoukankan      html  css  js  c++  java
  • 设计模式(二):观察者模式

    一、概述

      观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式有时成为发布/订阅模式,就是让多个对象在一个对象的状态改变时被通知到。

    二、解决问题

      当一个系统有多个类协同工作,如果在一个类中需要知道另外一个类的实现细节才能让系统运转,就会导致系统耦合过紧,以后相互依赖的类改变了或者新增了依赖的类,很多类需要同时更改。为了让交互对象之间能够松耦合,我们可以使用观察者模式。

    三、结构类图

     

    四、成员角色

      抽象主题(Subject):把所有对观察者对象的引用保存到一个集合中,每个抽象主题角色都有任何数量的观察者。抽象主题角色提供一个接口,可以增加和删除观察者。一般用一个抽象类和接口实现。

      抽象观察者(Observer):为所有具体观察者定义一个接口,在得到主题的通知时更新自己。

      具体主题(ConcreteSubject):继承抽象主题,在具体主题内部状态改变时,给所有登记过的观察者发出通知。

      具体观察者(ConcreteObserver):实现抽象观察者所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者可以保存一个指向具体主题的引用。

    五、应用实例

      下面是银行账号资金变动下发通知的例子,只要账号资金变动,就会同时下发app客户端消息、邮件消息、短信消息通知客户账号资金变动了

      //主题接口

    public interface Subject {
    	public void registerObserver(Observer o);
    	public void removeObserver(Observer o);
    	public void notifyObservers();
    }
    

      //抽象主题

    abstract public class AbstractSubject implements Subject{
    	//可以在这里新增Subject没有的方法,以便子类共用
    }
    

      //观察者接口

    public interface Observer {
    	public void update(double money);
    }
    

      //app客户端观察者

    public class APPObserver implements Observer{
    	public void update(double money){
    		//发送app客户端通知账号资金变动
    		System.out.println("app客户端提醒:您的账户资金变动:" + money);
    	}
    }
    

      //邮件观察者

    public class MailObserver implements Observer{
    	public void update(double money){
    		//发送邮件通知账号资金变动
    		System.out.println("邮件提醒:您的账户资金变动:" + money);
    	}
    }
    

      //短信观察者

    public class SmsObserver implements Observer{
    	public void update(double money){
    		//发送短信通知账号资金变动
    		System.out.println("短信提醒:您的账户资金变动:" + money);
    	}
    }
    

      //客户银行账号主题

    public class AccountSubject extends AbstractSubject{
    	//观察者集合
    	private List<Observer> observers;
    	//资金变动
    	private double money;
    	
    	public AccountSubject(){
    		//主题实例化时,初始化观察者集合
    		observers = new ArrayList<Observer>();
    	}
    	
    	//账号资金变动时通知观察者
    	public void notifyObservers() {
    		if(observers.size() > 0){
    			for(Observer observer:observers){
    				observer.update(money);
    			}
    		}
    		
    	}
    
    	//注册观察者
    	public void registerObserver(Observer o) {
    		observers.add(o);
    	}
    
    	//删除观察者
    	public void removeObserver(Observer o) {
    		int i = observers.indexOf(o);
    		if(i > 0){
    			observers.remove(i);
    		}
    		
    	}
    	
    	//改变账户资金
    	public void changeAccount(double money){		
    		System.out.println("账户资金变动:" + money);
    		this.money = money;
    		
    		//通知观察者
    		notifyObservers();
    	}
    
    }
    

      //测试观察者

    public class TestObserver {
    	public static void main(String[] args){
    		//创建账号主题
    		AccountSubject subject = new AccountSubject();
    		
    		//创建观察者
    		Observer appObserver = new APPObserver();
    		Observer smsObserver = new SmsObserver();
    		Observer mailObserver = new MailObserver();
    		
    		//注册观察者到账号主题
    		subject.registerObserver(appObserver);
    		subject.registerObserver(mailObserver);
    		subject.registerObserver(smsObserver);	
    		
    		subject.changeAccount(7.8);
    	}
    }
    

      

    测试结果:

    其实 Java内置了观察者模式,我们可以直接使用java.util包中的Obserber接口和Observable类实现观察者模式。下面我们把上面的代码稍微修改,用Java内置的观察者模式实现银行账号资金变动下发各种通知。

      //首先我们创建被观察者

    import java.util.Observable;
    
    public class AccountSubject extends Observable{
    	
    	//资金变动数额
    	private double money;
    	
    	public AccountSubject(){
    	}
    	
    	//改变账户资金
    	public void changeAccount(double money){		
    		System.out.println("(测试java自带观察者模式)账户资金变动:" + money);
    		this.money = money;
    		
    		//被观察者状态改变
    		this.setChanged(); 
    		//java内部实现推拉通知,使用时直接调用notifyObservers(),
    		//或者notifyObservers(Object o),传递的参数会在观察者类接收到
    		notifyObservers();
    	}
    
    	public double getMoney() {
    		return money;
    	}
    
    	public void setMoney(double money) {
    		this.money = money;
    	}	
    
    }
    

      //app客户端观察者

    import java.util.Observable;
    import java.util.Observer;
    
    public class APPObserver implements Observer{
    	//在观察者类图中我们看到,观察者可以持用被观察者的引用
    	Observable observale;
    	
    	public APPObserver(Observable observable){
    		this.observale = observable;
    		//直接调用java自带的方法,把观察者注册到被观察者
    		observable.addObserver(this);
    	}
    
    	//具体观察者实现Observer接口的方法,
    	//arg1参数就是被观察者的notifyObservers(Object o)传过来的参数
    	public void update(Observable arg0, Object arg1) {
    		if(arg0 instanceof AccountSubject){
    			AccountSubject accountSubject = (AccountSubject)arg0;
    			//发送app客户端通知账号资金变动
    			System.out.println("app客户端提醒:您的账户资金变动:" + accountSubject.getMoney());
    		}
    		
    	}
    }
    

      //邮件观察者

    import java.util.Observable;
    import java.util.Observer;
    
    public class MailObserver implements Observer{
    	
    	//在观察者类图中我们看到,观察者可以持用被观察者的引用
    	Observable observale;
    	
    	public MailObserver(Observable observable){
    		this.observale = observable;
    		//直接调用java自带的方法,把观察者注册到被观察者
    		observable.addObserver(this);
    	}
    	
    	//java的观察者有自带的update方法
    	public void update(Observable arg0, Object arg1) {
    		if(arg0 instanceof AccountSubject){
    			AccountSubject accountSubject = (AccountSubject)arg0;
    			//发送app客户端通知账号资金变动
    			System.out.println("邮件提醒:您的账户资金变动:" + + accountSubject.getMoney());
    		}
    	}
    
    }
    

      //短信观察者

    import java.util.Observable;
    import java.util.Observer;
    
    public class SmsObserver implements Observer{
    	
    	//在观察者类图中我们看到,观察者可以持用被观察者的引用
    	Observable observale;
    	
    	public SmsObserver(Observable observable){
    		this.observale = observable;
    		//直接调用java自带的方法,把观察者注册到被观察者
    		observable.addObserver(this);
    	}
    
    	//java的观察者有自带的update方法
    	public void update(Observable arg0, Object arg1) {
    		if(arg0 instanceof AccountSubject){
    			AccountSubject accountSubject = (AccountSubject)arg0;
    			//发送app客户端通知账号资金变动
    			System.out.println("短信提醒:您的账户资金变动:" + + accountSubject.getMoney());	
    		}			
    	}
    }
    

      //测试java自带观察者模式

    import java.util.Observer;
    
    public class TestObserver {
    	public static void main(String[] args){
    		//创建被观察者,也就是账号主题
    		AccountSubject observble = new AccountSubject();
    				
    		//创建观察者
    		Observer appObserver = new APPObserver(observble);
    		Observer smsObserver = new SmsObserver(observble);
    		Observer mailObserver = new MailObserver(observble);
    		
    		//账号资金变动
    		observble.changeAccount(7.8);
    	}
    }
    

      测试结果:

    六、优点与缺点

      1.优点

      (1)、观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖抽象,而不是依赖具体。从而使各自的变化都不会影响到另一方。主题并不知道任何一个具体的观察者,只知道他们有一个共同的接口。

      (2)、观察者模式支持“广播通信”。主题会向所有的观察者发出通知。

      (3)、观察者模式符合“对修改关闭,对扩展开放”的设计原则。

      2.缺点

      (1)、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会话费很多时间。

      (2)、观察者只知道被观察者发生了变化,但并不知道被观察者是如何发生变化的。

    七、使用场景

      1、一个抽象模型有两个方面,其中一个方面依赖另一个方面。将这些方面封装在独立的对象中使它们可以各自独立的改变和复用。

      2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少个对象将发生改变,可以降低对象之间的耦合度。

      3、一个对象必须通知其他对象,但而不知道这些对象是谁。就是对象之间的松耦合

    八、总结

      1、观察者模式定义了对象之间一对多关系。

      2、有多个观察者时,不可以依赖特定的通知次序。

      3、java有通用的观察者模式(java.util.Observable)。

      4、观察者与被观察者之间用松耦合方式结合。

      5、简单理解就是一个对象状态的改变要通知到其它对象。

     

  • 相关阅读:
    WPF:改变ListBoxItem和ListViewItem的颜色
    WPF样式——经典博客
    WPF中的Style(风格,样式)
    WPF中的动画
    WPF StoryBoard用法
    WPF 单个触发器、多个触发器、多条件触发器
    WPF编程学习 —— 样式
    WFP学习好微博
    Style、ControlTemplate 和 DataTemplate 触发器
    Arcgis 中国区技术支持 官网
  • 原文地址:https://www.cnblogs.com/jenkinschan/p/5677552.html
Copyright © 2011-2022 走看看