深入理解观察者模式(一)

来源:互联网 发布:python 内存地址 编辑:程序博客网 时间:2024/06/05 21:03

观察者模式是最常用的设计模式式之一,在程序开发中经常会用到,特别是在像Android,IOS等基于GUI的系统开发中。理解,并掌握观察者模式能够帮助我们解决很多应用开发过程中遇到的问题。

在学习每一种设计模式之前应该考虑清楚,模式的意图,解决什么问题,有什么优缺点,以及怎么合理地应用到具体的项目开发中。今天我就围绕着几个问题来讲解观察者模式。

在软件系统中,存在一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动。为了更好地描述对象之间存在的这种一对多(包括一对一)的联动,观察者模式应运而生,它定义了对象之间一种一对多的依赖关系,让一个对象的改变能够影响其他对象。(关键词:为了实现对象之间的联动)

看了上述话,我们应该知道观察者模式要解决的问题是如何实现对象之间的联动,那么意图就是要为实现对象之间的联动提供一套完整的解决方案。

明确了观察者模式要解决的问题和意图后,我们看看它的具体实现:
下面是观察者模式的类图:

这里写图片描述

AbstractSubject:抽象主题类,即被观察者的抽象定义。将增删观察者,通知观察者更新,以及设置主题是否发生了改变的行为都定义在抽象主题类里面,这样的好处是可以实现代码复用,具体主题类继承之后就不需要再重写这一块逻辑,只需要关注自己的业务逻辑实现。

AbstractObserver:抽象观察者。抽象观察者通过接口实现,因为抽象观察者只需要定义对象的更新行为,不同观察者更新逻辑不一样,所以必须延伸到子类做具体实现。

ConcreteSubject:具体主题类。

ConcreteObserver:具体观察者类。

下面来看看具体代码:
1.AbsSubject类

package com.observer.standard;import java.util.ArrayList;import java.util.List;public abstract class AbsSubject {    private List<AbsObserver> observers;//观察者集合    private boolean changed=false;//标志被观察对象是否发生改变    public AbsSubject() {        super();        observers=new ArrayList<AbsObserver>();    }    public void addObserver(AbsObserver o){        observers.add(o);    }    public void deleteObserver(AbsObserver o){        if(observers.size()==0){            return;        }        observers.remove(o);    }    public void notifyUpdate(){        notifyUpdate(null);    }    public void notifyUpdate(Object obj){        if(changed){            for(AbsObserver o:observers){                o.update(this, obj);            }        }    }    public boolean isChanged() {        return changed;    }    public void setChanged(boolean changed) {        this.changed = changed;    }}

2.AbsObserver 类

package com.observer.standard;public interface AbsObserver {    public void update(AbsSubject s,Object o);}

3.ConcreteSubject 类

package com.observer.standard;public class ConcreteSubject extends AbsSubject {    //写具体的业务逻辑}

4.ConcreteObserver 类

package com.observer.standard;public class ConcreteObserver implements AbsObserver {    @Override    public void update(AbsSubject s, Object o) {        System.out.println("update");    }}

5.Client类

package com.observer.standard;public class Client {    public static void main(String[] args) {        AbsSubject s=new ConcreteSubject();        AbsObserver o=new ConcreteObserver();        s.addObserver(o);        s.setChanged(true);//设置观察目标发生了改变        s.notifyUpdate();    }}

我们来分析这样实现观察者模式的优缺点:

优点:
1.将抽象主题通过抽象类实现,可以在抽象主题中实现一些通用逻辑,比如增删观察者,通知观察者更新,设置主题状态等等,实现代码复用。如果用接口实现,那么每个子类都得自己实现这部分的逻辑。

2.观察者的update方法中传了两个参数,既把需要通知的信息传给了观察者,也把抽象主题对象的引用传给了观察者,可以同时实现了观察者模式的推模型和拉模型,当我们要实现拉模型就调用notifyUpdate(),要实现推模型就调用notifyUpdate(Object obj)。

3.如果某个主题要增加新的观察者,只需要新写一个具体观察者类,实现观察者接口,不需要对原有代码进行修改,符合开闭原则。

4.在主题通知观察者更新时可以判断主题对象是否有发生改变,有改变才通知更新,这样可以避免主题执行某些操作后并没有引起任何改变而通知观察者更新。如果没有这样的限制在GUI程序中可能会导致屏幕数据没改变的情况下刷新界面,影响用户体验。

5.观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。

缺点:
1.主题对象为了实现复用通过抽象类实现,但是java只支持单继承,如果继承抽象主题就不能再继承其他类了,可能会影响到具体主题类以后的扩展

2.具体主题类应尽量符合单一职责原则,即只能有一个引起它变化的原因,因为这样能保证所有观察者都是监听被观察目标同一种变化引起的更新行为。这样做的原因主要是因为主题类一变化必须通知所有观察者进行更新,如果在具体主题类里面有多个引起它变化的原因,并且每一个变化都有相应观察者监听,那么一旦主题类发生了某种变化,那么监听不同变化的观察者都得跟着更新,如果处理逻辑不当很容易出现错误。但是实际开发中设计符合单一职则的类有时候比较困难,这时候如果又不想对具体主题类进行重构,那么可能有两种妥协的方法:
第一种,在具体主题类里面声明多组观察者集合,不同组的观察者监听主题类的不同变化。这样的弊端是具体主题必须依赖于多组抽象观察者集合,具体观察者如果要监听主题类的多种变化必须实现不同的观察者接口。
第二种,在观察者接口中定义多个更新方法,不同的方法是对具体主题类不同变化的更新操作。因为这样的接口是针对具体主题类的,但是并不是所有观察者都要监听具体主题类的所有变化,而每个子类都得实现所有方法,可能会产生很多空实现。
当然这样子也就变得不通用了,只能设计针对具体主题类的观察者模式。

3.如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。

4.虽然观察者可以获得抽象主题类的引用,但是想通过该引用获得关于具体主题类的更多信息还是有限的,毕竟抽象主题类只封装通用的数据,如果想获得具体主题类的信息,还是得让观察者接口依赖于具体主题类会更好些。如果抽象主题类通过接口实现就可以达到这种效果。

java中其实已经提供了实现观察者模式的api,实现方式跟我讲的这个差不多,也都符合我上面所讲的优点和缺点。在具体开发中如果可以直接用java的api实现观察者模式那最好用java的api,毕竟自己写的没人家好。如果实在不想用继承方式实现抽象主题类的话,再自己去写个通过接口实现抽象主题的观察者模式。

前面说了java只支持单继承,所以,在某些情况下我们还是得把抽象主题类通过接口来实现。我这里也写了个用接口实现的观察者模式:

package com.observer.standard.in;/** * @author hyh * 抽象主题接口 */public interface ISubject {    public void addObserver(IObserver observer);    public void deleteObserver(IObserver observer);    public void notifyUpdate() ;    public void notifyUpdate(Object o) ;}
package com.observer.standard.in;/** * @author hyh *抽象观察者接口 */public interface IObserver {    /**     *      * @param subject:观察的目标     * @param o:额外信息     */    public void update(ISubject subject, Object o);}
package com.observer.standard.in;import java.util.ArrayList;import java.util.List;import java.util.Observer;/** * @author hyh * */public class Subject1 implements ISubject{    private String name;    private boolean changed;    private List<IObserver> observers;    public Subject1(String name) {        super();        this.name = name;        observers=new ArrayList<IObserver>();    }    @Override    public void notifyUpdate(Object o) {            for(IObserver observer:observers){                observer.update(this,o);            }    }    @Override    public void addObserver(IObserver observer){        observers.add(observer);    }    @Override    public void deleteObserver(IObserver observer){        observers.remove(observer);    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public boolean isChanged() {        return changed;    }    public void setChanged(boolean changed) {        this.changed = changed;    }    @Override    public void notifyUpdate() {        notifyUpdate(null);    }}
package com.observer.standard.in;import java.util.ArrayList;import java.util.List;import java.util.Observer;/** * @author hyh * */public class Subject2 implements ISubject{    private String name;    private boolean changed;    private List<IObserver> observers;    public Subject2(String name) {        super();        this.name = name;        observers=new ArrayList<IObserver>();    }    @Override    public void notifyUpdate(Object o) {            for(IObserver observer:observers){                observer.update(this,o);            }    }    public void addObserver(IObserver observer){        observers.add(observer);    }    public void deleteObserver(IObserver observer){        observers.remove(observer);    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public boolean isChanged() {        return changed;    }    public void setChanged(boolean changed) {        this.changed = changed;    }    @Override    public void notifyUpdate() {        notifyUpdate(null);    }}
package com.observer.standard.in;/** * @author hyh * */public class Observer1 implements IObserver {    @Override    public void update(ISubject subject,Object o) {        //强转成具体观察目标,抽象观察目标使用接口而不是使用抽象类实现的不好之处就是得强转        Subject1 s=(Subject1) subject;        System.out.println("\n我是1号观察者,只有观察目标有改变我才更新");        if(s.isChanged()){            System.out.println("观察目标已经改变,进行更新");            System.out.println("观察目标的名字:"+s.getName());            System.out.println("收到的信息:"+o);        }else{            System.out.println("观察目标没改变,保持现状");        }    }}
package com.observer.standard.in;/** * @author hyh * */public class Observer2 implements IObserver {    @Override    public void update(ISubject subject,Object o) {        Subject1 s=(Subject1) subject;        System.out.println("\n我是2号观察目标,无论观察目标有没有改变我都会更新");        System.out.println("进行更新");        System.out.println("观察目标的名字:"+s.getName());        System.out.println("收到的信息:"+o);    }}
package com.observer.standard.in;/** * @author hyh * */public class Observer3 implements IObserver {    @Override    public void update(ISubject subject,Object o) {        Subject2 s=(Subject2) subject;        System.out.println("\n我是3号观察者,只有观察目标有改变我才更新");        if(s.isChanged()){            System.out.println("观察目标已经改变,进行更新");            System.out.println("观察目标的名字:"+s.getName());            System.out.println("收到的信息:"+o);        }else{            System.out.println("观察目标没改变,保持现状");        }    }}
package com.observer.standard.in;/** * @author hyh * */public class Observer4 implements IObserver {    @Override    public void update(ISubject subject,Object o) {        Subject2 s=(Subject2) subject;        System.out.println("\n我是4号观察者,无论观察目标有没有改变我都会更新");        System.out.println("进行更新");        System.out.println("观察目标的名字:"+s.getName());        System.out.println("收到的信息:"+o);    }}
package com.observer.standard.in;public class Client {    public static void main(String[] args) {            Subject1 subject1=new Subject1("subject1");            IObserver observer1=new Observer1();            IObserver observer2=new Observer2();            subject1.addObserver(observer1);            subject1.addObserver(observer2);            subject1.setChanged(false);            subject1.notifyUpdate("hello");            Subject2 subject2=new Subject2("subject2");            IObserver observer3=new Observer3();            IObserver observer4=new Observer4();            subject2.addObserver(observer3);            subject2.addObserver(observer4);            subject2.setChanged(true);            subject2.notifyUpdate("world");    }}

运行结果图:
这里写图片描述

通过接口实现需要注意的是在观察者的update方法中需要对抽象主题对象强转成具体主题对象,这样才可以拿到具体主题对象的一些属性值,也就是我们本来抽象观察者是依赖于抽象主题的,现在要变成依赖于具体主题类。抽象主题类用接口实现的好处有:
1.避开了java单继承的弊端
2.抽象观察者依赖于具体主题,这样可以拿到被观察目标的更多信息
当然也有不好的,比如具体主题类实现抽象主题接口时必须实现一部分相同的逻辑,不能实现代码复用。

0 0