设计模式-观察者模式

来源:互联网 发布:淘宝分享链接打不开 编辑:程序博客网 时间:2024/05/17 09:22

1.观察者模式介绍

    观察者模式是一个使用频率非常高的模式,它最常用的是GUI系统、订阅-发布系统。因为这个模式的重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。

2.观察者模式的定义

    定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

3.观察者模式的使用场景

    关联行为场景

        关联行为是可以拆分的,而不是“组合”的关系

    事件多级触发场景

    跨系统的消息交换场景

        如 消息队列、事件总线的处理机制

4.观察者模式的UML类图


5.具体实现

    抽象主题角色类

    /**     * 抽象主题类     * Created by Teaphy     * 2016/4/24.     */    public abstract class Subject {        // 用来保存注册的观察者对象        private List<Observer> listsObserver = new ArrayList<>();        /**         * 注册观察者对象         * @param observer    观察者对象         */        public void attach(Observer observer){            listsObserver.add(observer);            System.out.println("Attached an observer");        }        /**         * 删除观察者对象         * @param observer    观察者对象         */        public void detach(Observer observer){            listsObserver.remove(observer);        }        /**         * 通知所有注册的观察者对象         */        public void nodifyObservers(String newState){            for(Observer observer : listsObserver){                observer.update(newState);            }        }    }

    具体主题角色类

    /**     * 具体主题类     * Created by Teaphy     * 2016/4/24.     */    public class ConcreteSubject extends Subject {        private String msg;        public String getMsg() {            return msg;        }        public void setMsg(String msg) {            this.msg = msg;        }        public void publish(String msg) {            nodifyObservers(msg);        }    }

    抽象观察者角色类

    /**     * 抽象观察者角色类     * Created by Teaphy     * 2016/4/24.     */    public interface Observer {        /**         * 更新接口         * Created By         * on 2016/4/24         * @param msg 更新信息         */        void update(String msg);    }

    具体观察者角色

   /**     * 具体观察者角色     * Created by Teaphy     * 2016/4/24.     */    public class ObserverA implements Observer {        /**         * 更新观察者信息         */        public void update(String msg) {            System.out.println("ObserverA - msg = [" + msg + "]");        }    }            /**     * 具体观察者角色     * Created by Teaphy     * 2016/4/24.     */    public class ObserverB implements Observer {        /**         * 更新观察者信息         */        public void update(String msg) {            System.out.println("ObserverB - msg = [" + msg + "]");        }    }            /**     * 具体观察者角色     * Created by Teaphy     * 2016/4/24.     */    public class ObserverC implements Observer {        /**         * 更新观察者信息         */        public void update(String msg) {            System.out.println("ObserverC - msg = [" + msg + "]");        }    }

    测试

    /**     * Created by Teaphy     * 2016/4/24.     */    public class TestObserver {        public static void main(String[] args) {            //创建主题对象            ConcreteSubject cs = new ConcreteSubject();            // 添加观察者对象            cs.attach(new ObserverA());            cs.attach(new ObserverB());            cs.attach(new ObserverC());            // 发布新版本            cs.publish("英语");        }    }

     输出结果

    Attached an observer    Attached an observer    Attached an observer    ObserverA - msg = [英语]    ObserverB - msg = [英语]    ObserverC - msg = [英语]

6.推模型和拉模型

    在观察者模式中,又分为推模型和拉模型两种方式。
  ● 推模型
            主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
  ●拉模型
            主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
    根据上面的描述,发现前面的例子就是典型的推模型,下面给出一个拉模型的实例。    

    拉模型的抽象观察者类

    拉模型通常都是把主题对象当做参数传递。
  
 /**     * 抽象观察者角色类     * Created by Teaphy     * 2016/4/24.     */    public interface ObserverPull {        /**         * 更新接口         * Created By         * on 2016/4/24         * @param subjectPull 传入主题对象,方面获取相应的主题对象的状态         */        void update(SubjectPull subjectPull);    }

    拉模型的具体观察者类

   
/**     * 具体观察者角色     * Created by Teaphy     * 2016/4/24.     */    public class ObserverPullA implements ObserverPull {        private String msg;        public String getMsg() {            return msg;        }        public void setMsg(String msg) {            this.msg = msg;        }        /**         * 更新观察者信息         */        public void update(SubjectPull subjectPull) {            setMsg(((ConcreteSubjectPull)subjectPull).getMsg());        }    }

    拉模型的抽象主题类

    拉模型的抽象主题类主要的改变是nodifyObservers()方法。在循环通知观察者的时候,也就是循环调用观察者的update()方法的时候,传入的参数不同了
/**     * 抽象主题类     * Created by Teaphy     * 2016/4/24.     */    public abstract class SubjectPull {        // 用来保存注册的观察者对象        private List<ObserverPull> listsObserver = new ArrayList<>();        /**         * 注册观察者对象         * @param observer    观察者对象         */        public void attach(ObserverPull observer){            listsObserver.add(observer);            System.out.println("Attached an observer");        }        /**         * 删除观察者对象         * @param observer    观察者对象         */        public void detach(ObserverPull observer){            listsObserver.remove(observer);        }        /**         * 通知所有注册的观察者对象         */        public void nodifyObservers(){            for(ObserverPull observer : listsObserver){                observer.update(this);            }        }    }

    拉模型的具体主题类

  跟推模型相比,有一点变化,就是调用通知观察者的方法的时候,不需要传入参数了
  
 /**     * 具体主题类     * Created by Teaphy     * 2016/4/24.     */    public class ConcreteSubjectPull extends SubjectPull {        private String msg;        public String getMsg() {            return msg;        }        public void setMsg(String msg) {            this.msg = msg;        }        public void publish(String msg) {            setMsg(msg);            //状态发生改变,通知各个观察者            nodifyObservers();        }    }

    测试

    /**     * Created by Teaphy     * 2016/4/24.     */    public class TestObserverPull {        public static void main(String[] args) {            //创建主题对象            ConcreteSubjectPull cs = new ConcreteSubjectPull();            ObserverPullA observerPullA = new ObserverPullA();            ObserverPullB observerPullB = new ObserverPullB();            ObserverPullC observerPullC = new ObserverPullC();            // 添加观察者对象            cs.attach(observerPullA);            cs.attach(observerPullB);            cs.attach(observerPullC);            // 发布新版本            cs.publish("英语周报20160414期");            System.out.println("ObserverPullA - " + observerPullA.getMsg());            System.out.println("ObserverPullB - " + observerPullB.getMsg());            System.out.println("---------------------------------");            // 发布新版本            cs.publish("英语周报20160424期");            System.out.println("ObserverPullA - " + observerPullA.getMsg());            System.out.println("ObserverPullC - " + observerPullC.getMsg());        }    }

    输出结果

    ObserverPullA - 英语周报20160414期    ObserverPullB - 英语周报20160414期    ---------------------------------    ObserverPullA - 英语周报20160424期    ObserverPullC - 英语周报20160424期

7.两种模式的比较

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

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

0 0