• java设计模式之观察者模式


    好久没有写博客了,这段时间在项目中接触到了java设计模式中的观察者模式,因为之前从来没有接触过,所以特意花费了一点时间用来学习,在这里也把它记录下来,毕竟好记性不如烂笔头,闲言少叙,话不多说,书归正传。

    一、概念(来自百度百科)

    观察者模式:(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式),是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

    百度百科里的概念过于正式,换做自己的话来说,观察者模式就相当于java里面的一对多的关系,多的一方称之为观察者,可以是一,也可以是多,一的一方也被称为被观察者,用现实中的某样事物来比喻的话,个人觉得更像是报纸,被观察者就相当于报纸公司,它在报纸中发布各种信息,然后每一名读者,也就是购买报纸的人,就会收到这条信息。

    二、案例

    首先需要定义一个被观察者的抽象类

    package com.eplugger.test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 抽象公司类(作为被观察者,也就是消息发送者)
     */
    public abstract class Company {
        // 观察者的集合,也就是订阅该报纸的所有读者
        private List<Person> observers = new ArrayList<Person>();
        
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Company(String name) {
            this.name = name;
        }
        
        // 新增观察者(读者)
        public void addObservers(Person observer){
            observers.add(observer);
        }
        
        // 移除观察者(读者)
        public void removeObservers(Person observer){
            observers.remove(observer);
        }
        
        // 通知观察者(读者)
        public void notifyObservers(){
            for (Person observer : observers) {
                observer.update();
            }
        }
    
    }

    接着,定义观察者的抽象类

    package com.eplugger.test;
    
    /**
     * 定义抽象观察者
     * 
     * */
    public abstract class Person {
        
        private String name;
        private Company company;
        
        public String getName() {
            return name;
        }
        
        public Company getCompany() {
            return company;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public void setCompany(Company company) {
            this.company= company;
        }
    
        public Person(String name, Company company) {
            this.name = name;
            this.company = company;
        }
        
        public abstract void update();
    
    }

    下面是两者的具体实现类

    package com.eplugger.test;
    /**
     * 具体的消息通知者(报社)
     * 
     * */
    public class Newspaper extends Company{
    
        public Newspaper(String name){
            super(name);
        }
        
        private String title;
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
        
    }
    package com.eplugger.test;
    /**
     * 具体观察者(读者)
     * 
     * */
    public class Readers extends Person{
    
        public Readers(String name, Newspaper newspaper) {
            super(name, newspaper);
        }
    
        @Override
        public void update(){
            System.out.println(super.getName()+"...."+super.getCompany().getName()+"....通知....");
        }
    }

    测试代码

    package com.eplugger.test;
    
    public class Test1 {
    
        public static void main(String[] args) {
            Newspaper newspaper = new Newspaper("新华日报");
            Readers readers = new Readers("张三", newspaper);
            
            newspaper.removeObservers(readers);
            newspaper.notifyObservers();
        }
    }

    上面描述的这是一种最基本,最简单的观察者模式,是建立在所有的观察者都能抽象出一个公共抽象类的基础上,但是在实际的项目运用中,往往没有办法做到所有的观察者都能够抽象出同一个公共抽象类,这就得用到Java自带的Observer接口以提供对观察者模式的支持,同时也会运用到JDK来实现观察者模式。

    在Java中,已经有现成的接口和类,对于观察者,也就是消息接收者,Java提供了一个接口,源码如下

    package java.util;
    
    /**
     * A class can implement the <code>Observer</code> interface when it
     * wants to be informed of changes in observable objects.
     *
     */
    public interface Observer {
    
        void update(Observable o, Object arg);
    }

    对于被观察者,也就是消息的发布者,Java提供了一个抽象类,源码如下

    package java.util;
    
    /**
     * This class represents an observable object, or "data"
     * in the model-view paradigm. It can be subclassed to represent an
     * object that the application wants to have observed.
     * <p>
     * An observable object can have one or more observers. An observer
     * may be any object that implements interface <tt>Observer</tt>. After an
     * observable instance changes, an application calling the
     * <code>Observable</code>'s <code>notifyObservers</code> method
     * causes all of its observers to be notified of the change by a call
     * to their <code>update</code> method.
     * <p>
     * The order in which notifications will be delivered is unspecified.
     * The default implementation provided in the Observable class will
     * notify Observers in the order in which they registered interest, but
     * subclasses may change this order, use no guaranteed order, deliver
     * notifications on separate threads, or may guarantee that their
     * subclass follows this order, as they choose.
     * <p>
     * Note that this notification mechanism is has nothing to do with threads
     * and is completely separate from the <tt>wait</tt> and <tt>notify</tt>
     * mechanism of class <tt>Object</tt>.
     * <p>
     * When an observable object is newly created, its set of observers is
     * empty. Two observers are considered the same if and only if the
     * <tt>equals</tt> method returns true for them.
     *
     */
    public class Observable {
        private boolean changed = false;
        private Vector obs;
    
        /** Construct an Observable with zero Observers. */
    
        public Observable() {
            obs = new Vector();
        }
    
        /**
         * Adds an observer to the set of observers for this object, provided
         * that it is not the same as some observer already in the set.
         * The order in which notifications will be delivered to multiple
         * observers is not specified. See the class comment.
         *
         * @param   o   an observer to be added.
         * @throws NullPointerException   if the parameter o is null.
         */
        public synchronized void addObserver(Observer o) {
            if (o == null)
                throw new NullPointerException();
            if (!obs.contains(o)) {
                obs.addElement(o);
            }
        }
    
        /**
         * Deletes an observer from the set of observers of this object.
         * Passing <CODE>null</CODE> to this method will have no effect.
         * @param   o   the observer to be deleted.
         */
        public synchronized void deleteObserver(Observer o) {
            obs.removeElement(o);
        }
    
        /**
         * If this object has changed, as indicated by the
         * <code>hasChanged</code> method, then notify all of its observers
         * and then call the <code>clearChanged</code> method to
         * indicate that this object has no longer changed.
         * <p>
         * Each observer has its <code>update</code> method called with two
         * arguments: this observable object and <code>null</code>. In other
         * words, this method is equivalent to:
         * <blockquote><tt>
         * notifyObservers(null)</tt></blockquote>
         *
         */
        public void notifyObservers() {
            notifyObservers(null);
        }
    
        /**
         * If this object has changed, as indicated by the
         * <code>hasChanged</code> method, then notify all of its observers
         * and then call the <code>clearChanged</code> method to indicate
         * that this object has no longer changed.
         * <p>
         * Each observer has its <code>update</code> method called with two
         * arguments: this observable object and the <code>arg</code> argument.
         *
         */
        public void notifyObservers(Object arg) {
            /*
             * a temporary array buffer, used as a snapshot of the state of
             * current Observers.
             */
            Object[] arrLocal;
    
            synchronized (this) {
                /* We don't want the Observer doing callbacks into
                 * arbitrary code while holding its own Monitor.
                 * The code where we extract each Observable from
                 * the Vector and store the state of the Observer
                 * needs synchronization, but notifying observers
                 * does not (should not).  The worst result of any
                 * potential race-condition here is that:
                 * 1) a newly-added Observer will miss a
                 *   notification in progress
                 * 2) a recently unregistered Observer will be
                 *   wrongly notified when it doesn't care
                 */
                if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
    
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(this, arg);
        }
    
        /**
         * Clears the observer list so that this object no longer has any observers.
         */
        public synchronized void deleteObservers() {
            obs.removeAllElements();
        }
    
        /**
         * Marks this <tt>Observable</tt> object as having been changed; the
         * <tt>hasChanged</tt> method will now return <tt>true</tt>.
         */
        protected synchronized void setChanged() {
            changed = true;
        }
    
        /**
         * Indicates that this object has no longer changed, or that it has
         * already notified all of its observers of its most recent change,
         * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
         * This method is called automatically by the
         * <code>notifyObservers</code> methods.
         *
         */
        protected synchronized void clearChanged() {
            changed = false;
        }
    
        /**
         * Tests if this object has changed.
         *
         * @return  <code>true</code> if and only if the <code>setChanged</code>
         *          method has been called more recently than the
         *          <code>clearChanged</code> method on this object;
         *          <code>false</code> otherwise.
         */
        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();
        }
    }

    比较一下Java的源码和上述我自己写的区别,可以看到,源码中使用Vector而不是ArrayList,相比而言,Vector是线程安全的,其次,在添加和删除观察者时对两个方法使用了synchronized关键字,这都是因为多线程的原因,可以看出来,源码考虑的要全面的多。

    利用Java提供的接口和抽象类观察者模式和上述我自写的差不多,就放一个小demo,简单的看一下就ok了。

    Demo

    package com.eplugger.demo;
    
    import java.util.Observable;
    import java.util.Observer;
    
    public class Readers implements Observer{
        
        public Readers(Observable o){
            o.addObserver(this);
        }
    
        @Override
        public void update(Observable o, Object arg) {
            System.out.println("名字是:" + ((Newspaper)o).getName());    
        }
    }
    package com.eplugger.demo;
    
    import java.util.Observable;
    
    public class Newspaper extends Observable {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            if (!this.name.equals(name)){
                this.name = name;
                setChanged();
            }
            notifyObservers();
        }
    }
    package com.eplugger.demo;
    
    public class Test {
    
        public static void main(String[] args) {
            Newspaper newspaper = new Newspaper();
            Readers readers = new Readers(newspaper);
            
            newspaper.setName("新华日报");
        }
    }

    运用Java自带的接口和抽象类来实现观察者模式,其原理和上一个案例差不多。

    三、总结

    观察者模式的结构

    观察者模式的优点:

    1、实现了表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,抽象了接口,使得可以适应不同的表现层充当观察者角色。

    2、在观察目标和观察者之间建立了一个抽象耦合,观察目标只需要维持一个抽象观察者的集合,无需了解具体的观察者。

    3、支持广播通信,观察目标会向所有的观察者对象发送通知,简化了一对多系统设计的难度。

    4、满足“开闭原则“的要求,增加新的具体观察者无需修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

    观察者模式的缺点:

    1、如果一个被观察者有着很多观察者,那么会耗费很长的时间。

    2、没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

    3、 Java中自带的观察者模式,被观察者是一个抽象类而不是接口,限制了复用能力。

    应用场景:

    一个对象的改变将会导致一个或多个对象的改变的情况,即一对多的程序设计。

  • 相关阅读:
    Java实现 LeetCode 640 求解方程(计算器的加减法计算)
    PHP zip_entry_compressedsize() 函数
    PHP zip_entry_close() 函数
    PHP zip_close() 函数
    PHP xml_set_unparsed_entity_decl_handler() 函数
    PHP xml_set_processing_instruction_handler() 函数
    格列隙 | grid-row-gap (Grid Layout)
    格列隙 | grid-column-gap (Grid Layout)
    格列端 | grid-column-end (Grid Layout)
    格列开始 | grid-column-start (Grid Layout)
  • 原文地址:https://www.cnblogs.com/libinhyq/p/11772763.html
Copyright © 2020-2023  润新知