对象间的联动-观察者模式

观察者模式定义如下: 定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
观察者模式结构:

图22-3 观察者模式结构图

在观察者模式结构图中包含如下几个角色:

● Subject(目标):目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。

● ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。如果无须扩展目标类,则具体目标类可以省略。

● Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。

● ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法。通常在实现时,可以调用具体目标类ConcreteSubject的attach()方法将自己添加到目标类的集合中或通过detach()方法将自己从目标类的集合中删除。

抽象目标Subject,典型代码如下所示:

import java.util.*;  
abstract class Subject {  
    //定义一个观察者集合用于存储所有观察者对象  
protected ArrayList observers<Observer> = new ArrayList();  

//注册方法,用于向观察者集合中增加一个观察者  
    public void attach(Observer observer) {  
    observers.add(observer);  
}  

    //注销方法,用于在观察者集合中删除一个观察者  
    public void detach(Observer observer) {  
    observers.remove(observer);  
}  

    //声明抽象通知方法  
    public abstract void notify();  
}  
具体目标类:
class ConcreteSubject extends Subject {  
    //实现通知方法  
    public void notify() {  
        //遍历观察者集合,调用每一个观察者的响应方法  
        for(Object obs:observers) {  
            ((Observer)obs).update();  
        }  
    }     
}

抽象观察者Observer典型代码如下所示:

interface Observer {  
    //声明响应方法  
    public void update();  
}  

具体观察者:

class ConcreteObserver implements Observer {  
    //实现响应方法  
    public void update() {  
        //具体响应代码  
    }  
}

23.3 完整解决方案

为了实现对象之间的联动,Sunny软件公司开发人员决定使用观察者模式来进行多人联机对战游戏的设计,其基本结构如图22-4所示:

图22-4 多人联机对战游戏结构图 

在图22-4中,AllyControlCenter充当目标类,ConcreteAllyControlCenter充当具体目标类,Observer充当抽象观察者,Player充当具体观察者。

抽象观察类:

//抽象观察类  
interface Observer {  
    public String getName();  
    public void setName(String name);  
    public void help(); //声明支援盟友方法  
    public void beAttacked(AllyControlCenter acc); //声明遭受攻击方法  
}  

具体观察者:

//战队成员类:具体观察者类  
class Player implements Observer {  
    private String name;  

    public Player(String name) {  
        this.name = name;  
    }  

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

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

    //支援盟友方法的实现  
    public void help() {  
        System.out.println("坚持住," + this.name + "来救你!");  
    }  

    //遭受攻击方法的实现,当遭受攻击时将调用战队控制中心类的通知方法notifyObserver()来通知盟友  
    public void beAttacked(AllyControlCenter acc) {  
        System.out.println(this.name + "被攻击!");  
        acc.notifyObserver(name);         
    }  
}  

抽象目标类:

abstract class AllyControlCenter {  
    protected String allyName; //战队名称  
    protected ArrayList<Observer> players = new ArrayList<Observer>(); //定义一个集合用于存储战队成员  

    public void setAllyName(String allyName) {  
        this.allyName = allyName;  
    }  

    public String getAllyName() {  
        return this.allyName;  
    }  

    //注册方法  
    public void join(Observer obs) {  
        System.out.println(obs.getName() + "加入" + this.allyName + "战队!");  
        players.add(obs);  
    }  

    //注销方法  
    public void quit(Observer obs) {  
        System.out.println(obs.getName() + "退出" + this.allyName + "战队!");  
        players.remove(obs);  
    }  

    //声明抽象通知方法  
    public abstract void notifyObserver(String name);  
}  

具体目标类:

//具体战队控制中心类:具体目标类  
class ConcreteAllyControlCenter extends AllyControlCenter {  
    public ConcreteAllyControlCenter(String allyName) {  
        System.out.println(allyName + "战队组建成功!");  
        System.out.println("----------------------------");  
        this.allyName = allyName;  
    }  

    //实现通知方法  
    public void notifyObserver(String name) {  
        System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭受敌人攻击!");  
        //遍历观察者集合,调用每一个盟友(自己除外)的支援方法  
        for(Object obs : players) {  
            if (!((Observer)obs).getName().equalsIgnoreCase(name)) {  
                ((Observer)obs).help();  
            }  
        }         
    }  
}

客户端代码

class Client {  
    public static void main(String args[]) {  
        //定义观察目标对象  
AllyControlCenter acc;  
        acc = new ConcreteAllyControlCenter("金庸群侠");  

        //定义四个观察者对象  
        Observer player1,player2,player3,player4;  

        player1 = new Player("杨过");  
        acc.join(player1);  

        player2 = new Player("令狐冲");  
        acc.join(player2);  

        player3 = new Player("张无忌");  
        acc.join(player3);  

        player4 = new Player("段誉");  
        acc.join(player4);  

        //某成员遭受攻击  
        Player1.beAttacked(acc);  
    }  
}

编译并运行程序,输出结果如下:

 

金庸群侠战队组建成功!
----------------------------
杨过加入金庸群侠战队!
令狐冲加入金庸群侠战队!
张无忌加入金庸群侠战队!
段誉加入金庸群侠战队!
杨过被攻击!
金庸群侠战队紧急通知,盟友杨过遭受敌人攻击!
坚持住,令狐冲来救你!
坚持住,张无忌来救你!
坚持住,段誉来救你!

22.4 JDK对观察者模式的支持

 在JDK的java.util包中,提供了Observable类以及Observer接口,它们构成了JDK对观察者模式的支持。如图22-5所示:

(1) Observer接口

在java.util.Observer接口中只声明一个方法,它充当抽象观察者,其方法声明代码如下所示:

void  update(Observable o, Object arg);

当观察目标的状态发生变化时,该方法将会被调用,在Observer的子类中将实现update()方法,即具体观察者可以根据需要具有不同的更新行为。当调用观察目标类Observable的notifyObservers()方法时,将执行观察者类中的update()方法。

(2) Observable类

java.util.Observable类充当观察目标类,在Observable中定义了一个向量Vector来存储观察者对象,它所包含的方法及说明见表22-1: 表22-1 Observable类所包含方法及说明

方法名方法描述Cool
Observable() 构造方法,实例化Vector向量。 $1600
addObserver(Observer o) 用于注册新的观察者对象到向量中。 $12
deleteObserver (Observer o) 用于删除向量中的某一个观察者对象。 $1
notifyObservers()和notifyObservers(Object arg) 通知方法,用于在方法内部循环调用向量中每一个观察者的update()方法。  
deleteObservers() 用于清空向量,即删除向量中所有观察者对象。  
setChanged() 该方法被调用后会设置一个boolean类型的内部标记变量changed的值为true,表示观察目标对象的状态发生了变化。  
clearChanged() 用于将changed变量的值设为false,表示对象状态不再发生改变或者已经通知了所有的观察者对象,调用了它们的update()方法。  
hasChanged() 用于测试对象状态是否改变。  
countObservers() 用于返回向量中观察者的数量。

 我们可以直接使用Observer接口和Observable类来作为观察者模式的抽象层,再自定义具体观察者类和具体观察目标类,通过使用JDK中的Observer接口和Observable类,可以更加方便地在Java语言中应用观察者模式。

22.6 观察者模式与MVC

 MVC是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。其中模型可对应于观察者模式中的观察目标,而视图对应于观察者,控制器可充当两者之间的中介者。当模型层的数据发生改变时,视图层将自动改变其显示内容。如图22-7所示:

图22-7 MVC结构示意图

在图22-7中,模型层提供的数据是视图层所观察的对象,在视图层中包含两个用于显示数据的图表对象,一个是柱状图,一个是饼状图,相同的数据拥有不同的图表显示方式,如果模型层的数据发生改变,两个图表对象将随之发生变化,这意味着图表对象依赖模型层提供的数据对象,因此数据对象的任何状态改变都应立即通知它们。同时,这两个图表之间相互独立,不存在任何联系,而且图表对象的个数没有任何限制,用户可以根据需要再增加新的图表对象,如折线图。在增加新的图表对象时,无须修改原有类库,满足“开闭原则”。

 

posted @ 2019-01-18 17:31  Archer-Fang  阅读(272)  评论(0)    收藏  举报