观察者模式_观察者模式指令 - CSDN
观察者模式 订阅
观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。 展开全文
观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
信息
模    式
软件设计模式
外文名
Observer Mode
又称为
发布-订阅模式
中文名
观察者模式
系    统
通常被用来实现事件处理系统
观察者模式基本介绍
观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主题是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。观察者模式不仅被广泛应用于软件界面元素之间的交互,在业务对象之间的交互、权限管理等方面也有广泛的应用。 [1]  观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
收起全文
精华内容
参与话题
  • 观察者模式详解

    千次阅读 2017-07-15 22:32:21
     观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。  观察者模式定义了一种一对多的依赖关系,让多...

    在阎宏博士的《Java与模式》一书中开头是这样描述观察者(Observer)模式的:

      观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

      观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。


    观察者模式的结构

      一个软件系统里面包含了各种对象,就像一片欣欣向荣的森林充满了各种生物一样。在一片森林中,各种生物彼此依赖和约束,形成一个个生物链。一种生物的状态变化会造成其他一些生物的相应行动,每一个生物都处于别的生物的互动之中。

      同样,一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。

      下面以一个简单的示意性实现为例,讨论观察者模式的结构。

      观察者模式所涉及的角色有:

      ●  抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

      ●  具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。

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

      ●  具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

      源代码

        抽象主题角色类

    复制代码
    public abstract class Subject {
        /**
         * 用来保存注册的观察者对象
         */
        private    List<Observer> list = new ArrayList<Observer>();
        /**
         * 注册观察者对象
         * @param observer    观察者对象
         */
        public void attach(Observer observer){
            
            list.add(observer);
            System.out.println("Attached an observer");
        }
        /**
         * 删除观察者对象
         * @param observer    观察者对象
         */
        public void detach(Observer observer){
            
            list.remove(observer);
        }
        /**
         * 通知所有注册的观察者对象
         */
        public void nodifyObservers(String newState){
            
            for(Observer observer : list){
                observer.update(newState);
            }
        }
    }
    复制代码

      具体主题角色类

    复制代码
    public class ConcreteSubject extends Subject{
        
        private String state;
        
        public String getState() {
            return state;
        }
    
        public void change(String newState){
            state = newState;
            System.out.println("主题状态为:" + state);
            //状态发生改变,通知各个观察者
            this.nodifyObservers(state);
        }
    }
    复制代码

      抽象观察者角色类

    复制代码
    public interface Observer {
        /**
         * 更新接口
         * @param state    更新的状态
         */
        public void update(String state);
    }
    复制代码

      具体观察者角色类

    复制代码
    public class ConcreteObserver implements Observer {
        //观察者的状态
        private String observerState;
        
        @Override
        public void update(String state) {
            /**
             * 更新观察者的状态,使其与目标的状态保持一致
             */
            observerState = state;
            System.out.println("状态为:"+observerState);
        }
    
    }
    复制代码

      客户端类

    复制代码
    public class Client {
    
        public static void main(String[] args) {
            //创建主题对象
            ConcreteSubject subject = new ConcreteSubject();
            //创建观察者对象
            Observer observer = new ConcreteObserver();
            //将观察者对象登记到主题对象上
            subject.attach(observer);
            //改变主题对象的状态
            subject.change("new state");
        }
    
    }
    复制代码

      运行结果如下

      在运行时,这个客户端首先创建了具体主题类的实例,以及一个观察者对象。然后,它调用主题对象的attach()方法,将这个观察者对象向主题对象登记,也就是将它加入到主题对象的聚集中去。

      这时,客户端调用主题的change()方法,改变了主题对象的内部状态。主题对象在状态发生变化时,调用超类的notifyObservers()方法,通知所有登记过的观察者对象。

    推模型和拉模型

      在观察者模式中,又分为推模型和拉模型两种方式。

      ●  推模型

         主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。

      ●  拉模型

         主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

      根据上面的描述,发现前面的例子就是典型的推模型,下面给出一个拉模型的实例。

      拉模型的抽象观察者类

      拉模型通常都是把主题对象当做参数传递。

    复制代码
    public interface Observer {
        /**
         * 更新接口
         * @param subject 传入主题对象,方面获取相应的主题对象的状态
         */
        public void update(Subject subject);
    }
    复制代码

      拉模型的具体观察者类

    复制代码
    public class ConcreteObserver implements Observer {
        //观察者的状态
        private String observerState;
        
        @Override
        public void update(Subject subject) {
            /**
             * 更新观察者的状态,使其与目标的状态保持一致
             */
            observerState = ((ConcreteSubject)subject).getState();
            System.out.println("观察者状态为:"+observerState);
        }
    
    }
    复制代码

      拉模型的抽象主题类

      拉模型的抽象主题类主要的改变是nodifyObservers()方法。在循环通知观察者的时候,也就是循环调用观察者的update()方法的时候,传入的参数不同了。

    复制代码
    public abstract class Subject {
        /**
         * 用来保存注册的观察者对象
         */
        private    List<Observer> list = new ArrayList<Observer>();
        /**
         * 注册观察者对象
         * @param observer    观察者对象
         */
        public void attach(Observer observer){
            
            list.add(observer);
            System.out.println("Attached an observer");
        }
        /**
         * 删除观察者对象
         * @param observer    观察者对象
         */
        public void detach(Observer observer){
            
            list.remove(observer);
        }
        /**
         * 通知所有注册的观察者对象
         */
        public void nodifyObservers(){
            
            for(Observer observer : list){
                observer.update(this);
            }
        }
    }
    复制代码

      拉模型的具体主题类

      跟推模型相比,有一点变化,就是调用通知观察者的方法的时候,不需要传入参数了。

    复制代码
    public class ConcreteSubject extends Subject{
        
        private String state;
        
        public String getState() {
            return state;
        }
    
        public void change(String newState){
            state = newState;
            System.out.println("主题状态为:" + state);
            //状态发生改变,通知各个观察者
            this.nodifyObservers();
        }
    }
    复制代码

      两种模式的比较

      ■  推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。

      ■  推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。


    JAVA提供的对观察者模式的支持

      在JAVA语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。

      Observer接口

      这个接口只定义了一个方法,即update()方法,当被观察者对象的状态发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法。

    public interface Observer {
    
        void update(Observable o, Object arg);
    }

      Observable类

      被观察者类都是java.util.Observable类的子类。java.util.Observable提供公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要:一个是setChanged(),另一个是notifyObservers()。第一方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。

    复制代码
    public class Observable {
        private boolean changed = false;
        private Vector obs;
       
        /** Construct an Observable with zero Observers. */
    
        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);
        }
    
        /**
         * 如果本对象有变化(那时hasChanged 方法会返回true)
         * 调用本方法通知所有登记的观察者,即调用它们的update()方法
         * 传入this和arg作为参数
         */
        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();
        }
    
        /**
         * 将“已变化”设置为true
         */
        protected synchronized void setChanged() {
        changed = true;
        }
    
        /**
         * 将“已变化”重置为false
         */
        protected synchronized void clearChanged() {
        changed = false;
        }
    
        /**
         * 检测本对象是否已变化
         */
        public synchronized boolean hasChanged() {
        return changed;
        }
    
        /**
         * Returns the number of observers of this <tt>Observable</tt> object.
         *
         * @return  the number of observers of this object.
         */
        public synchronized int countObservers() {
        return obs.size();
        }
    }
    复制代码

      这个类代表一个被观察者对象,有时称之为主题对象。一个被观察者对象可以有数个观察者对象,每个观察者对象都是实现Observer接口的对象。在被观察者发生变化时,会调用Observable的notifyObservers()方法,此方法调用所有的具体观察者的update()方法,从而使所有的观察者都被通知更新自己。

    怎样使用JAVA对观察者模式的支持

      这里给出一个非常简单的例子,说明怎样使用JAVA所提供的对观察者模式的支持。在这个例子中,被观察对象叫做Watched;而观察者对象叫做Watcher。Watched对象继承自java.util.Observable类;而Watcher对象实现了java.util.Observer接口。另外有一个Test类扮演客户端角色。

      源代码

      被观察者Watched类源代码

    复制代码
    public class Watched extends Observable{
        
        private String data = "";
        
        public String getData() {
            return data;
        }
    
        public void setData(String data) {
            
            if(!this.data.equals(data)){
                this.data = data;
                setChanged();
            }
            notifyObservers();
        }
        
        
    }
    复制代码

     

      观察者类源代码

    复制代码
    public class Watcher implements Observer{
        
        public Watcher(Observable o){
            o.addObserver(this);
        }
        
        @Override
        public void update(Observable o, Object arg) {
            
            System.out.println("状态发生改变:" + ((Watched)o).getData());
        }
    
    }
    复制代码

      测试类源代码

    复制代码
    public class Test {
    
        public static void main(String[] args) {
            
            //创建被观察者对象
            Watched watched = new Watched();
            //创建观察者对象,并将被观察者对象登记
            Observer watcher = new Watcher(watched);
            //给被观察者状态赋值
            watched.setData("start");
            watched.setData("run");
            watched.setData("stop");
    
        }
    
    }
    复制代码

      Test对象首先创建了Watched和Watcher对象。在创建Watcher对象时,将Watched对象作为参数传入;然后Test对象调用Watched对象的setData()方法,触发Watched对象的内部状态变化;Watched对象进而通知实现登记过的Watcher对象,也就是调用它的update()方法。

    展开全文
  • 设计模式(十二)观察者模式

    千次阅读 2018-09-25 21:52:45
    老板回来时,前台会打电话到公司里,以防老板发现员工们在看股票。代码实现? 版本一 双向耦合的代码 前台秘书类: class Secretary{ // 同事列表 private IList&... private sting action...

    老板回来时,前台会打电话到公司里,以防老板发现员工们在看股票。代码实现?

    版本一  双向耦合的代码

    前台秘书类:

    class Secretary{
        // 同事列表
        private IList<StockObserver> observers = new List<StockObserver>();
        private sting action;
        // 增加
        // 有几个同事请前台帮忙就给集合增加几个对象
        public void Attach(StockObserver observer){
            observers.Add(observer);
        }
        
        // 通知
        // 待老板来时,就给所有的登记的同事们发通知“老板来了”
        public void Notify(){
            foreach(StockObserver o in obervers){
                o.Update();
            }
        }
        // 前台状态
        // 前台通过电话,所说的话或所做的事
        public string SecretaryAction{
            get{return action;}
            set{action=value;}
        }
    }

     看股票同事类:

    class StockObserver{
        private string name;
        private Secretary sub;
        public StockObserver(string name, Secretary sub){
            this.name=name;
            this.sub=sub;
        }
        public void Update(){
            Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SecretaryAction, name);
        }
    }

    客户端程序:

    static void Main(string[] args){
        // 前台小姐童童
        Secretary tongtong = new Secretary();
        // 看股票的同事
        StockObserver tongshi1 = new StockObserver("猪猪",tongtong);
        StockObserver tongshi2 = new StockObserver("猫猫",tongtong);
    
        // 前台记下了两位同事
        tongtong.Attach(tongshi1);
        tongtong.Attach(tongshi2);
        // 发现老板回来
        tongtong.SecretaryAction = "老板回来了!";
        // 通知两个同事
        tongtong.Notify();
    
        Console.Read();
    }

    写得不错,但是有没有发现?这个前台类和看股票类是互相耦合的——前台类要增加观察者,观察者类需要前台的状态。比如,如果观察者中还有人是想看NBA,前台类代码要怎么做?

     

    版本二  解耦实践1(开放-封闭、依赖倒转)

    增加了抽象的观察者:

    abstract class Observer{
        protected string name;
        protected Secretary sub;
    
        public Observer(string name,Secretary sub){
            this.name=name;
            this.sub=sub;
        }
    
        public abstract void Update();
    }

    增加了两个具体的观察者:

    // 看股票的同事
    class StockObserver{
        public StockObserver(string name, Secretary sub):base(name,sub)
        {}
        public override void Update(){
            Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SecretaryAction, name);
        }
    }
    
    // 看NBA的同事
    class NBAObserver{
        public NBAObserver(string name, Secretary sub):base(name,sub)
        {}
        public override void Update(){
            Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SecretaryAction, name);
        }
    }

    这里让两个观察者去继承抽象观察者,对于Update的方法做重写操作。

    下面是前台秘书类的编写,把所有的与具体观察者耦合的地方都改成了抽象观察者。

    class Secretary{
        // 同事列表
        private IList<Observer> observers = new List<Observer>();
        private sting action;
        // 增加
        // 针对抽象编程,减少了与具体类的耦合
        public void Attach(Observer observer){
            observers.Add(observer);
        }
        
        // 减少
        // 针对抽象编程,减少了与具体类的耦合
        public void Detach(Observer observer){
            observers.Remove(observer);
        }
    
        // 通知
        public void Notify(){
            foreach(StockObserver o in obervers){
                o.Update();
            }
        }
        // 前台状态
        public string SecretaryAction{
            get{return action;}
            set{action=value;}
        }
    }

    客户端代码跟前面一样。 

    可是这样考虑问题还不够全面——“前台秘书”也是一个具体的类,也应该抽象出来。即观察者不应该依赖具体的实现,而是一个抽象的通知者。

     

    版本三  解耦事件2 

    增加了抽象通知者接口:

    // 通知者接口
    interface Subject{
        void Attach(Observer observer);
        void Detach(Observer observer);
        void Notify();
        string SubjectState{
            get;
            set;
        }
    }

    具体的通知者类可能是前台,也可能是老板,它们也许有各自的一些方法,但对于通知者来说,他们都是一样的,所以它们都去实现这个接口。

    class Boss:Subject{
        // 同事列表
        private IList<Observer> observers = new List<Observer>();
        private sting action;
    
        // 增加
        public void Attach(Observer observer){
            observers.Add(observer);
        }
        
        // 减少
        public void Detach(Observer observer){
            observers.Remove(observer);
        }
    
        // 通知
        public void Notify(){
            foreach(Observer o in obervers){
                o.Update();
            }
        }
        // 老板状态
        public string SecretaryState{
            get{return action;}
            set{action=value;}
        }
    }

     前台类似。

    对于具体的观察者,需更改的地方就是把与“前台”耦合的地方都改成针对抽象通知者。

    abstract class Observer{
        protected string name;
        protected Subject sub;
    
        public Observer(string name,Subject sub){
            this.name=name;
            this.sub=sub;
        }
    
        public abstract void Update();
    }
    // 看股票的同事
    class StockObserver{
        public StockObserver(string name, Subject sub):base(name,sub)
        {}
        public override void Update(){
            Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SubjectState, name);
        }
    }
    
    // 看NBA的同事
    class NBAObserver{
        public NBAObserver(string name, Subject sub):base(name,sub)
        {}
        public override void Update(){
            Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectState, name);
        }
    }

    代码结构图:

     

    观察者模式

    观察者模式又叫做发布-订阅(Publish/Subscribe)模式 ,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

     

    Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或一个接口实现,它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

    abstract class Subject{
        private IList<Observer> observers = new List<Observer>();
    
        // 增加观察者
        public void Attach(Observer observer){
            observers.Add(observer);
        }
        
        // 减少
        public void Detach(Observer observer){
            observers.Remove(observer);
        }
    
        // 通知
        public void Notify(){
            foreach(StockObserver o in obervers){
                o.Update();
            }
        }
    }

    Oberser类,抽象观察者。为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个Upadate()方法,这个方法叫做更新方法。

    abstract class Observer{
        public abstract void Update();
    }

    ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。

    class ConcreteSubject : Subject{
        private string subjectState;
        // 具体被观察者状态
        public string SubjectState{
            get{return subjectState;}
            set{subjectState = value;}
        }
    }

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

    class ConcreteObserver : Observer{
        private string name;
        private string observerState;
        private ConcreteSubject subject;
    
        public ConcreteObserver(ConcreteSubject subject, string name){
            this.subject=subject;
            this.name=name;
        }
    
        public override void Update(){
            observerState=subject.SubjectState;
            Console.WriteLine("观察者{0}的新状态是{1}", name, observerState);
    
        }
    
        public ConcreteSubject Subject{
            get{return subject;}
            set{subject = value;}
        }
    }

    客户端代码:

    static void Main(string[] args){
        ConcreteSubject s = new ConcreteSubject();
    
        s.Attach(new ConcreteObserver(s,"X"));
        s.Attach(new ConcreteObserver(s,"Y"));
        s.Attach(new ConcreteObserver(s,"Z"));
    
        s.SubjectState = "ABC";
        s.Notify();
        
        Console.Read();
    }

    结果显示:

     

    将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不变。而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。

    那么,什么时候考虑使用观察者模式呢?

    一个对象的改变需要同时改变其他对象,而且,它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。

    当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用

    总的来讲,观察者模式所做的工作其实就是在解除耦合让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边的变化。——这是依赖倒转原则的最佳体现!

     

    观察者模式的不足

    尽管用了依赖倒转原则,但是“抽象通知者”还是依赖“抽象观察者”,也就是说,万一没有了抽象观察者这样的接口,这通知的功能就完不成了。另外就是每个具体观察者不一定是“更新”的方法要调用,有可能每个具体观察者需要调用的方法都不一样。

    如果通知者和观察者之间根本就互相不知道,由客户端来决定通知谁,那就好了——

    还记得我们序章的内容么?没错,就是事件委托! 

     

    事件委托

    委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的”类“,委托的实例将代表一个具体的函数。一个委托可以搭载多个方法,所有方法被依次唤起。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。

     这就使得本来在“老板”类中的增加和减少的抽象观察者集合以及通知时遍历的抽象观察者都不必要了。转到客户端来让委托搭载多个方法,这就解决了本来与抽象观察者的耦合问题。

    但委托也是有前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型

     

     

     本章完。

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 本文是连载文章,此为第十二章,学习将让多个观察者对象同时监听某一个主题对象,当这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己的观察者模式,该模式充分体现了依赖倒置原则,解除了耦合。同时,我们还复习了事件委托。 

    上一章:https://blog.csdn.net/qq_36770641/article/details/82818444  建造者模式

    下一章:

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

    展开全文
  • 观察者模式简单实现

    千次阅读 2019-03-20 14:19:00
    观察者模式:观察者(Observer)关心被观察者(Subject)的某个状态(subjectState),一旦状态变化,观察者就能收到通知(Notify),并更新自己的状态(observerState)。 动机:在软件构建 过程中,我们需要为...

    设计模式目录
    观察者模式观察者模式:观察者(Observer)关心被观察者(Subject)的某个状态(subjectState),一旦状态变化,观察者就能收到通知(Notify),并更新自己的状态(observerState)。

    动机:在软件构建 过程中,我们需要为某些对象建立一种“通知依赖关系” 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面 向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
    意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
    与其他模式关系
    1.与访问模式有类似的地方,但访问者模式是双向依赖,二维变化。而观察者是单向的。

    观察者模式通常是一对多的关系,但我为了体现该模式的重点(即:被观察者一旦变化,观察者能自动的适应变化,把这种依赖固话,用以弱化这种依赖关系),所以我没有体现一对多的关系(但你要知道1是被观察者,多是观察者)。
    https://blog.csdn.net/xie__jin__cheng/article/details/87933229(这里实现了一对多关系的代码)
    如果是初学者,建议照着类图自己写一遍代码,比看10页别人写的说明更有用。
    代码如下:

    #include <iostream>
    #include <string>
    #include <list>
    using namespace std;
    
    class Observer
    {	
    public:
    	virtual void Update()=0;
    };
    //Observer::index = 0;
    class Subject
    {
    public:
    	Observer* pObserver;
    	void Atatch(Observer* o){
    		pObserver = o;
    	}
    	void Detach(){
    		pObserver = 0;	
    	}
    	void Notify(){
    		pObserver->Update();					
    	}
    };
    // 具体层
    class ConcreateSubject:public Subject{
    public:
    	int subjectState;
    	int getState(){
    		return subjectState;
    	}
    };
    class ConcreateObserver:public Observer{
    public:
    	int observerState;
    	ConcreateSubject* pSubject;
    	virtual void Update(){
    		observerState = pSubject->getState();
    		cout<<"observerState:"<<observerState<<"\n";
    	}
    };
    
    
    //客户端调用
    int main()
    {
    	cout<<"观察者模式演示\n";
    	ConcreateSubject* subject = new ConcreateSubject();
    	ConcreateObserver* observer = new ConcreateObserver();
    	observer->pSubject = subject;
    	subject->Atatch(observer);
    	subject->Notify();
    
    	subject->subjectState = 5;
    	subject->Notify();
    
    
    	//看代码不用考虑以下内容
    	int cin_a;
    	cin>>cin_a;
    	return 0;
    }
    
    展开全文
  • 观察者模式总结

    千次阅读 2018-03-30 17:47:17
    观察者模式观察者模式还有一个名字叫发布-订阅模式,我们熟悉的消息队列如ActiveMQ,就是采用的这种设计模式,总的来说观察者模式主要难理解的地方有两个,一个是发布者对一个或多个观察者的管理,另一个就是实现...

    观察者模式

    观察者模式还有一个名字叫发布-订阅模式,我们熟悉的消息队列如ActiveMQ,就是采用的这种设计模式,总的来说观察者模式主要难理解的地方有两个,一个是发布者对一个或多个观察者的管理,另一个就是实现消息发布者和订阅者之间的解耦。

    比较简单的实例已经有相关博客总结的非常OK了,如这篇:观察者模式简单实例,该片博客中的实例虽然简单,但是却非常具有代表性,能较好的解释观察模式的几个概念。下面在这篇博客的基础上实现其实例,并对实例做一些简单的改动

    上类图:


    IPublish和ICustomer代码:

    public interface IPublisher {
    
        public void registerObserver(ICustomer o);
        public void removeObserver(ICustomer o);
        public void notifyObserver();
    
    }
    public interface ICustomer {
    
        public void receiveMessage(String message);
    
    }

    Publish和Customer代码:

    public class Publisher implements IPublisher {
    
        private List<ICustomer> observers;
        private String message;
    
        public Publisher(List<ICustomer> observers){
            this.observers = observers;
        }
    
        @Override
        public void registerObserver(ICustomer o) {
            observers.add(o);
        }
    
        @Override
        public void removeObserver(ICustomer o) {
            if(!observers.isEmpty()){
                observers.remove(o);
            }
        }
    
    
        @Override
        public void notifyObserver() {
            for(ICustomer o:this.observers){
                o.receiveMessage(message);
            }
        }
    
        /**
         * 设置了消息立即通知观察者
         * @param message
         */
        public void setInformation(String message){
            this.message = message;
            System.out.println("公众号更新消息如下:"+message);
    
            //通知观察者
            notifyObserver();
        }
    }
    public class Customer implements ICustomer{
    
        private String name;
        private String message;
    
        public Customer(String name){
            this.name = name;
        }
    
        @Override
        public void receiveMessage(String message) {
            this.message = message;
            read();
        }
    
        public void read(){
            System.out.println(name+":收到推送消息:"+message);
        }
    }

    测试实例:

    public class ClientTest {
    
        public static void main(String[] args) {
            //这里需要模拟一个发布者,多个订阅者
    
            //消息订阅者
            ICustomer cus01  = new Customer("body01");
            ICustomer cus02  = new Customer("body02");
            ICustomer cus03  = new Customer("body03");
    
            //消息发布者
            List<ICustomer> observers = new ArrayList<ICustomer>();
            observers.add(cus01);
            observers.add(cus02);
            observers.add(cus03);
    
            Publisher subscribe = new Publisher(observers);
            //更新了消息之后,会自动通知消费者
            subscribe.setInformation("message01 ---这是人性的扭曲还是道德的沦丧");
            System.out.println("cus01 取消关注公众号");
            observers.remove(cus01);
            subscribe.setInformation("message02 ---震惊,一群男女光天化日下尽然干这事......");
        }
    
    }

    运行结果:


    一个比较简单的实例,但是却将观察者模式的相关对象展示的比较清晰,回到文章的开头,观察模式会有两个难理解的地方,一个是针对多个订阅者的维护,上述实例是通过在发布者对象中维护一个list来完成对多个订阅者的维护,针对解耦,上述实例是定义了两个接口,消息发布接口和消息订阅者接口,两者都可以自动扩展自己的对象——如果用于需要关注另一个公众号,只需要在加一个公众号类实现消息发布接口即可。

    其实见到观察者模式,还有一个不得不提到的就是Listener(监听器),这在spring中也大量用到,这里需要进一步理解这个概念,如果提到监听器,第一反应就想到了之前C/S模式下的各种控件监听事件的编写,所以这里就利用一个简单的鼠标事件例子来进一步解释观察者模式

    类图:


    这个实例中Mouse就是其实就类似消息的发布者,点击了鼠标,相当于发出了消息,Event会根据事件类型去调用相应的处理方式,就是MouseEventCallBack,所以这里的MouseEventCallBack就相当于是订阅者,而其中的EventListener相当于是消息发布者维护消息订阅者的一张表而已。根据不同的事件去调动不同的Event对象,Event对象根据callback方法去调用MouseEventCallback中相应的方法。各个类的代码如下:

    Mouse

    public class Mouse extends EventListener{
    
        public void click(){
            System.out.println("鼠标单击");
            this.trigger(MouseEventType.ON_CLICK);
        }
    
        public void doubleClick(){
            System.out.println("鼠标双击");
            this.trigger(MouseEventType.ON_DOUBLE_CLICK);
        }
    
        public void up(){
            System.out.println("鼠标弹起");
            this.trigger(MouseEventType.ON_UP);
        }
    
        public void down(){
            System.out.println("鼠标按下");
            this.trigger(MouseEventType.ON_DOWN);
        }
    
        public void wheel(){
            System.out.println("鼠标滚动");
            this.trigger(MouseEventType.ON_WHEEL);
        }
    
        public void hover(){
            System.out.println("鼠标悬停");
            this.trigger(MouseEventType.ON_HOVER);
        }
    }

    MouseEventCallback

    public class MouseEventCallback {
    
        public void onClick(Event e){
            System.out.println("=================触发了鼠标单击事件=================\n");
        }
    
        public void onDoubleClick(Event e){
            System.out.println("=================触发了鼠标双击事件=================\n");
        }
    
        public void onUp(Event e){
            System.out.println("=================触发了鼠标弹起事件=================\n");
        }
    
        public void onDown(Event e){
            System.out.println("=================触发了鼠标下压事件=================\n");
        }
    
        public void onWheel(Event e){
            System.out.println("=================触发了鼠标滚动事件=================\n");
        }
    
        public void onHover(Event e){
            System.out.println("=================触发了鼠标悬停事件=================\n");
        }
    
        public void onMove(Event e){
            System.out.println("=================触发了鼠标移动事件=================\n");
        }
    
    }

    EventListener

    public class EventListener {
    
        protected Map<Enum,Event> events = new HashMap<Enum,Event>();
    
        public void addListener(Enum eventType,Object target,Method callback){
            //注册事件,待会儿会用反射来调用方法
            events.put(eventType,new Event(target,callback));
        }
    
        private void trigger(Event event){
            event.setSource(this);
            event.setTirggerTime(System.currentTimeMillis());
    
            try {
                //invoke的参数,第一个是object对象,第二个是方法
                event.getCallback().invoke(event.getTarget(),event);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        protected void trigger(Enum call){
            if(!this.events.containsKey(call)){
                return;
            }
            trigger(this.events.get(call).setTrigger(call.toString()));
        }
    }

    Event

    public class Event {
    
        //事件源
        private Object source;
        //通知目标
        private Object target;
        //回调
        private Method callback;
        //触发器
        private String trigger;
        //时间
        private long tirggerTime;
    
        public Event(Object target, Method callback) {
            this.target = target;
            this.callback = callback;
        }
    
        public Object getSource() {
            return source;
        }
    
        public Object getTarget() {
            return target;
        }
    
        public Method getCallback() {
            return callback;
        }
    
        public void  setCallback(Method callback) {
            this.callback = callback;
        }
    
        Event setSource(Object source) {
            this.source = source;
            return this;
        }
    
        protected void setTirggerTime(long tirggerTime) {
            this.tirggerTime = tirggerTime;
        }
    
        Event setTrigger(String trigger) {
            this.trigger = trigger;
            return this;
        }
    
        public String getTrigger() {
            return trigger;
        }
    
        public long getTirggerTime() {
            return tirggerTime;
        }
    
    
    
        @Override
        public String toString() {
            return "Event{" +
                    "\t source=" + source +
                    "\t, target=" + target +
                    "\t, callback=" + callback +
                    "\t, trigger='" + trigger + '\'' +
                    '}';
        }
    }

    MouseEventType——事件类型枚举

    public enum MouseEventType {
        ON_CLICK,
        ON_DOUBLE_CLICK,
        ON_UP,
        ON_DOWN,
        ON_WHEEL,
        ON_MOVE,
        ON_HOVER;
    }

    测试类:

    public class MouseTest {
    
        public static void main(String[] args) {
    
            try {
                //鼠标对象
                Mouse mouse = new Mouse();
    
                //鼠标事件的回调函数对象
                MouseEventCallback callback = new MouseEventCallback();
                //获得回调对象中的onClick事件处理函数
                Method onClick = callback.getClass().getMethod("onClick",Event.class);
    
                //加入监听绑定事件
                System.out.println(onClick.toString());
                mouse.addListener(MouseEventType.ON_CLICK,callback,onClick);
    
                Method onDoubleClick = callback.getClass().getMethod("onDoubleClick",Event.class);
                mouse.addListener(MouseEventType.ON_DOUBLE_CLICK,callback,onDoubleClick);
    
                //人为调用鼠标事件
                mouse.click();
                mouse.doubleClick();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    运行结果:


    说明:Mouse类继承了EventListener似乎就是表示鼠标类是一个消息发布者而已,EventListener的任务就是维护不同的事件类型,Event中的一些set方法为了简单,直接返回了this对象,这个有点不规范。EventListener中的一个trigger中是利用反射去调用的指定的响应函数,整个例子需要结合类图才能有更好的理解。

    在一定程度上上述简单的实例好像也可以用动态代理来实现,确实可以,某一种层度上MouseCallback就是mouse的增强,这个理解也算是正确吧,但是这里也是为了说明观察者模式而举得一个常用的实例。

    展开全文
  • 观察者模式

    千次阅读 2019-06-10 16:44:22
    观察者模式还算一个比较好玩的设计模式,其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。可以想象成消息中间件在系统中的作用。SpringBoot启动就是使用了观察者...
  • 在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机...
  • 观察者模式及其注意事项与缺点

    千次阅读 2019-05-16 21:32:15
    观察者模式定义了对象之间的一对多依赖关系,其中的“一”称为主题,而“多”称为观察者,若某个对象希望关注某个主题,则在该主题中进行注册,待有变化时便会通知(或更新)观察者。 各个观察者只要都继承自同一...
  • 发布订阅模式与观察者模式

    万次阅读 多人点赞 2019-03-29 18:25:12
    设计模式并非是软件开发的专业术语,实际上,“模式”最早诞生于建筑学。 设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。通俗一点说,设计模式是在某种场合下对某个问题的一种...
  • 观察者模式(Java代码)

    千次阅读 2018-01-27 00:17:06
    1.什么是观察者模式 2.要创建的三个接口 3.数据源的建立(天气观测站) 4.观察者的建立(观察者) (一)什么是观察者模式 观察者模式就如一个聊天室,当你需要收到聊天室的消息时,你就注册成为聊天室的成员,当聊天...
  • 这是之前做的“产品详情页”的长截图,这里讲解一下这个页面与“观察者模式”的关系:在活动季,底部的“开始预订按钮”会变成“秒杀按钮”(所谓秒杀按钮,就是一个显示成灰色不可点击,并且一直处于倒计时状态的...
  • 观察者模式(Observer Pattern)标签: 设计模式初涉描述性文字本节讲述的是:观察者模式(Observer Pattern),如果你用过RxJava,相信你对 这个模式应该不会陌生,当然,RxJava是扩展的观察者模式,这里不做了解,只 ...
  • 观察者模式:观察者(Observer)相当于事件监听者,被观察者(Observable)相当于事件源和事件,执行逻辑时通知observer即可触发oberver的update,同时可传被观察者和参数 监听器模式demo 1、首要定义事件源对象(事件...
  • 观察者模式的效果有以下的优点:第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。观察者模式有下面的缺点:第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很...
  • 大话设计模式(五)观察者模式

    万次阅读 2020-09-27 09:40:47
    大话设计模式(五)观察者模式概念 在阎宏博士的《JAVA与模式》一书中开头是这样描述观察者(Observer)模式的: 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-...
  • 观察者模式(Observer)和发布(Publish/订阅模式(Subscribe)的区别在翻阅资料的时候,有人把观察者(Observer)模式等同于发布(Publish)/订阅(Subscribe)模式,也有人认为这两种模式还是存在差异,而我认为...
  • 设计模式(五)观察者模式

    万次阅读 多人点赞 2017-12-31 05:39:49
    观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听莫一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
  • Java设计模式之观察者模式

    万次阅读 多人点赞 2015-04-16 09:18:53
    本文继续介绍23种设计模式系列。介绍的是观察者模式
  • 对象间的联动——观察者模式(二)

    万次阅读 多人点赞 2012-07-05 23:34:09
    22.2 观察者模式概述  观察者模式是使用频率最高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为...
  • JAVA设计模式--观察者模式

    万次阅读 2019-12-17 10:05:47
    一、什么是观察者模式 二、观察者模式的典型应用 三、观察者模式的结构 四、Java对观察者模式的支持 五、观察者模式的优点 一、什么是观察者模式 观察者(Observer)模式是行为模式之一,它的作用是当一个被观察...
  • 11种行为型模式之:观察者模式 (Observer)
1 2 3 4 5 ... 20
收藏数 246,219
精华内容 98,487
关键字:

观察者模式