设计模式系列之四:观察者模式

前言

观察者模式是属于设计模式中的行为型模式,所谓行为型就是指对象的动作发生改变,比如方法以及状态。那么观察者模式是一种什么模式呢?说白了,观察者模式解决的一对多的依赖关系,当一个对象的状态发生改变的时候,其他依赖此对象的对象会得到通知并且做出相应的改变。但从定义上还是很难理解。我们可以从一个简单的例子中更深地去体会观察者模式。

问题背景

某公司的两名职员在主管离开办公室后,一个在看股票,一个在玩游戏。公司有一个前台,该两名职员让前台当主管回来的时候通知他们不让主管看到他们在干其他的事。

编码实践一

根据上述场景,下面是我写出的第一版代码:

//抽象职员类
public abstract class Employee{
    protected String name;
    //通知者对象
    protected Notifyer notifyer;
    public Employee(){}
    public Employee(String name,Notifyer notifyer){
        this.name = name;this.notifyer = notifyer;
    }

    public void setName(String name){this.name = name;}
    public String getName(){return name;}

    //更新自己状态的方法,比如当收到前台的通知的时候应该停止看股票继续工作
    public abstract void update();
}

//看股票的职员
public class StockEmployee extends Employee{
    public StockEmployee(String name,Notifyer notifyer){super(name,notifyer);}

    public void update(){
        System.out.println("收到前台" + notifyer.getName() + "的" + notifyer.getMessage() + "的通知," + getName() + "停止看股票,继续工作!");
    }
}

//玩游戏的职员,与上面的代码类似
public class GameEmployee extends Employee{
    public GameEmployee(String name,Notifyer notifyer){super(name,notifyer);}

    public void update(){
        System.out.println("收到前台" + notifyer.getName() + "的" + notifyer.getMessage() + "的通知," + getName() + "停止玩游戏,继续工作!");
    }
}

//之所以创建一个通知者类主要是由于通知者与职员类执行的功能不同
public abstract class Notifyer{
    //具体的通知内容,比如主管回来了,就是一个具体的通知
    protected String name;
    protected String message;
    public Notifyer(String name){this.name = name;}
    //设置消息的内容
    protected void setMessage(String message){this.message = message;}
    protected String getMessage(){return message;}

    public String getName(){return name;}
    public void SetName(String name){this.name = name;}
    public abstract void notifyObserver();
}

public class Receptionist extends Notifyer{
    private List<Employee> observers = new ArrayList<Employee>();

    public Receptionist(String name){super(name);}
    //添加一个观察者,这里就是一个职员对象
    public void addObserver(Employee emp){observers.add(emp);}
    //移除一个观察者,这样被移除的观察者将收不到前台的通知,自然后果你懂得
    public void removeObserver(Employee emp){observers.remove(emp);}

    public void notifyObserver(){
        for(Employee emp : observers){
            emp.update();
        }
    }
}

//测试代码
public class Test{
    public static void main(String[] args){
        //创建一个前台
        Receptionist rep = om.new Receptionist("笑笑");
        rep.setMessage("主管回来啦");

        Employee stockEmp = om.new StockEmployee("子房",rep);
        Employee gameEmp = om.new GameEmployee("天明",rep);
        Employee gameEmp2 = om.new GameEmployee("少龙",rep);

        rep.addObserver(stockEmp);
        rep.addObserver(gameEmp);

        rep.notifyObserver();
    }
}

最后程序的运行结果如下:

我在程序使用的都是抽象类,抽象类是对类的抽象,类是对对象抽象。但是如果考虑到通知者有可能是完全不相关的对象,使用抽象类就不是很合理了,于是可以换成接口,接口是对类行为的抽象,通知者的核心职责就是告诉观察者具体的通知,其他的都不是最重要的。下面对代码进行进一步的的优化:

编码实践二

//修改抽象通知者对象为一个通知接口
public interface INotify{
    void addObserver(Employee emp);
    void removeObserver(Employee emp);
    void notifyObserver();
    String getName();
    String getMessage();
    void setMessage(String message);
    void setName(String name);
}

//修改前台的代码
public class Receptionist implements INotify{
    private List<Employee> observers = new ArrayList<Employee>();
    private String message;
    private String name;

    public Receptionist(String name,String message){
        this.name = name;
        this.message = message;
    }
    //添加一个观察者,这里就是一个职员对象
    public void addObserver(Employee emp){observers.add(emp);}
    //移除一个观察者,这样被移除的观察者将收不到前台的通知,自然后果你懂得
    public void removeObserver(Employee emp){observers.remove(emp);}

    public String getMessage(){return message;}
    public void setMessage(String message){this.message = message;}
    public String getName(){return name;}
    public void setName(String name){this.name = name;}

    public void notifyObserver(){
        for(Employee emp : observers){
            emp.update();
        }
    }
}

//增加一个新的通知者,比如闹钟(假定主管在10分钟后回来,自然在十分钟后看股票的职员就会收到闹钟的通知了)
public class AlarmClock implements INotify{
    private List<Employee> observers = new ArrayList<Employee>();
    private String message;

    private String name;

    public AlarmClock(String name,String message){
        this.name = name;
        this.message = message;
    }

    //添加一个观察者,这里就是一个职员对象
    public void addObserver(Employee emp){observers.add(emp);}
    //移除一个观察者,这样被移除的观察者将收不到前台的通知,自然后果你懂得
    public void removeObserver(Employee emp){observers.remove(emp);}

    public String getMessage(){return message;}
    public void setMessage(String message){this.message = message;}
    public String getName(){return name;}
    public void setName(String name){this.name = name;}

    public void notifyObserver(){
        for(Employee emp : observers){
            emp.update();
        }
    }
}

//注意在Employee及其派生类中的Notifyer对象要改成INotify对象,这里就不粘上代码了

//下面是测试代码
public class Test{
    public static void main(String[] args){
        //创建一个闹钟
        AlarmClock alarmClock = om.new AlarmClock("闹钟当当", "时间到啦");
        Employee stockEmp = om.new StockEmployee("子房",alarmClock);
        Employee gameEmp = om.new GameEmployee("天明",alarmClock);
        Employee gameEmp2 = om.new GameEmployee("少龙",alarmClock);

        alarmClock.addObserver(stockEmp);
        alarmClock.addObserver(gameEmp);

        alarmClock.notifyObserver();
    }
}

最后的测试结果是:

从以上两个测试结果可以发现,只要Notifyer的message属性发生改变,其所管辖的观察者的动作也发生了更新。注意到在测试代码中没有将第三个对象添加进来,这样当Notifyer的状态发生改变其动作不会发生更新。这样只有前两个对象发生了动作的更新。也就是说Notifyer这个对象的状态发生改变导致了其他两个对象的改变。而观察者模式也正是在这种情况下使用的。当一个对象的状态改变导致其他对象的状态也发生改变,而且不知道有多少具体的对象的状态要发生改变的时候,应该使用观察者模式。观察者模式有两个关键对象Subject(对应上面代码中的Notifyer和INotify对象)和Observer(对应上面的Employee对象)。Subject状态的改变会导致Observer对象的状态的改变一个Subject对象可以吧状态的改变通知给任意数目的观察者对象。一个Subject不需要知道具体的观察者,一个观察者也不需要知道其他观察者是否存在。实际上观察者模式主要是为了解耦(实际上23种设计模式中都体现了这一点,只不过不同的设计模式体现程度不同罢了),使得对象之间不依赖具体而是依赖抽象。这样设计的好处对具体的大修改不会影响其他依赖抽象的类,使得类之间的耦合度降低。

可以发现,在上面我们的代码中,具体的Subject是依赖Observer对象的,这样的依赖会不会影响呢?答案是肯定的。

虽然Subject和Observer都是抽象的,但是难保在系统中会一直存在这样抽象的Observer,所以这样的依赖已经最大程度减少了程序之间的耦合,但在这种情况下还是存在问题的。那么还没有其他的方法解决这种耦合呢?有。我们可以采用一种委托的事件机制完成进一步的解耦。事件委托可以将委托者的方法可以事件中去执行,事件委托仅仅需要的是要执行方法的对象、方法以及参数列表,这样事件委托对象就可以根据这三个参数执行制定对象的指定方法,从直观上理解,有点象代理,但不同于代理,由于在Java中没有现成的事件委托模型可以使用,可以实现一个自己的事件委托处理器。实现之后,我们在Subject中引入这个事件委托处理器,并添加相应的事件响应方法,而Observer对象不需要做任何改变。具体事件委托处理器的代码可以参考我的Github上的源码。

最后对观察者模式做一个简单的总结:

  1. 观察者模式主要适用于一个对象的改变会导致其他对象状态的改变的情况
  2. 使用抽象降低需要关联对象的耦合,不依赖具体而是依赖抽象
  3. 观察者模式的不足在于如果Observer对象的不再存在将对系统产生重大影响
时间: 2024-10-29 19:34:05

设计模式系列之四:观察者模式的相关文章

【设计模式系列】--观察者模式

在前面的博文中,小编介绍设计模式中的撩妹模式,不知道小伙伴们有没有从中get到技巧呢?今天这篇博文,咱们继续来学习设计模式的相关知识,今天小编向大家介绍的模式是观察者模式,还请小伙伴多多指教,小编会从什么是原型观察者模式.观察者模式的结构图.观察者模式的demo以及观察者模式的特点和应用场景等方面一一进行介绍,希望对有需要的小伙伴有帮助.      什么是观察者模式? 观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式.模型-视图(View)模式.源-收听者(Lis

设计模式 ( 十六 ) 观察者模式Observer(对象行为型)

设计模式 ( 十五 ) 观察者模式Observer(对象行为型)    1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来说,当某个对象的状态发生改变时,你仍然需要对象之间能互相通信.但是出于各种原因,你也许并不愿意因为代码环境的改变而对代码做大的修改.也许,你只想根据你的具体应用环境而改进通信代码.或者,你只想简单的重新构造通信代码来避免类和类之间的相互依赖与相互从属. 2.问题 当一个对象的状态发生改变时,你

大话设计模式系列

原文:大话设计模式系列 1.设计模式之前奏(UML类图) 2.设计模式之一(单例模式) 3.设计模式之二(简单工厂模式) 4.设计模式之三(工厂方法模式) 5.设计模式之四(抽象工厂模式第一回合) 6.设计模式之四(抽象工厂模式 第二回合) 7.设计模式之四(抽象工厂模式 第三回合) 8.设计模式原则(单一.开放封 闭.里氏代换.依赖倒转.迪米特法则五大原则) 9.设计模式之五(策略模式) 10.设计模式之六(装饰模式) 11.设计模式之七(代理模式) 12.设计模式之八(原型模式) 13.设计

ASP.NET企业开发框架IsLine FrameWork系列之四--DataProvider 数据访问(上)

DataProvider是日常编程中最常用的Provider,它为项目提供了与数据库交互的能力,使程序人员能 够方便.快捷的获得SQL结构,简化了程序代码,增强程序可读性.它共有8个类库,100多个方法,包括 配置.功能和枚举三部分,以下是它的基本类关系: 开发框架IsLine FrameWork系列之四--DataProvider 数据访问(上)-"> 图3.4公共接口 图3.5文件操作 图3.6 数据库操作与数据容器接口

ASP.net控件开发系列之四

属性与属性窗格 在上篇文章中,和大家探讨了属性和aspx文件中的HTML style 标签和文本的关系,遗漏了两点: 1.EnCodedInnerDefaultProperty和InnerDefaultProperty在使用中的区别,可能有些朋友对这个不是很清楚,EncodedInnerDefaultProperty属性是不允许内含控件对象的,比方说,声明了EncodedInnerDefaultProperty的DataList的Text属性是不允许你设为"<table ......>

.NET 4 并行(多核)编程系列之四 Task的休眠

原文:.NET 4 并行(多核)编程系列之四 Task的休眠 .NET 4 并行(多核)编程系列之四 Task的休眠 前言:之前的几篇文章断断续续的介绍了Task的一些功能:创建,取消.本篇介绍Task的休眠,本篇的内容比较的少. 本篇的议题如下: 1.       Task的休眠.   系列文章链接: .NET 4 并行(多核)编程系列之一入门介绍 .NET 4 并行(多核)编程系列之二 从Task开始  .NET 4 并行(多核)编程系列之三 从Task的取消  .NET 4 并行(多核)编

Android设计模式系列之工厂方法模式_Android

工厂方法模式,往往是设计模式初学者入门的模式,的确,有人称之为最为典型最具启发效果的模式. android中用到了太多的工厂类,其中有用工厂方法模式的,当然也有很多工厂并不是使用工厂方法模式的,只是工具管理类. 今天以ThreadFactory举例说明一下简单工厂模式和工厂方法模式. 工厂方法模式,Factory Method,简单的方式,不简单的应用. 1.意图 定义一个用于创建对象的接口,让子类决定实例化哪个类.工厂方式模式使一个类的实例化延迟到其子类. 热门词汇:虚构造器 延迟 创建对象

Android设计模式系列之组合模式_Android

Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用.在android UI设计,几乎所有的widget和布局类都依靠这两个类. 组合模式,Composite Pattern,是一个非常巧妙的模式.几乎所有的面向对象系统都应用到了组合模式. 1.意图 将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分). 组合模式使得用户对单个对象View和组合对象ViewGrou

Red Gate系列之四 SQL Data Compare 10.2.0.885 Edition 数据比较同步工具 完全破解+使用教程

原文:Red Gate系列之四 SQL Data Compare 10.2.0.885 Edition 数据比较同步工具 完全破解+使用教程 Red Gate系列之四 SQL Data Compare 10.2.0.885 Edition 数据比较同步工具 完全破解+使用教程 Red Gate系列文章: Red Gate系列之一 SQL Compare 10.2.0.1337 Edition 数据库比较工具 完全破解+使用教程 Red Gate系列之二 SQL Source Control 3.