设计模式之观察者模式

工作中我们经常会出现这样一种场景:在工作不忙的时候,老板出去办事了,然后很多员工就在工位上开始忙自己的事,比如看股票,看视频,看小说,吃零食。前台员工充当哨兵,只要老板一来,就马上通知员工们。

第一版,双向耦合代码实现:

//秘书充当前哨
public class Secretary {
    private List<StockObserver> observers = new ArrayList<StockObserver>();
    private String action;
    public void attach(StockObserver observer){
        observers.add(observer);
    }
    public void notifyObservers(){
        for(StockObserver so : observers){
            so.update();
        }
    }
    public String getAction() {
        return action;
    }
    public void setAction(String action) {
        this.action = action;
    }
}
public class StockObserver {
    private String name;
    private Secretary sub;
    public StockObserver(String name, Secretary sub){
        this.name = name;
        this.sub = sub;
    }
    public void update(){
        System.out.println(sub.getAction() + " " + name + " 关闭股票行情,继续工作!");
    }
}
public class Test {

    public static void main(String[] args) {
        //前台人员
        Secretary xxx = new Secretary();
        //看股票的同事
        StockObserver yyy = new StockObserver("xiaoming", xxx);
        StockObserver zzz = new StockObserver("xiaohua", xxx);
        
        //前台记下两位同事
        xxx.attach(zzz);
        xxx.attach(yyy);
        xxx.setAction("老板来了");
        xxx.notifyObservers();
    }

}

我们发现“前台”类与“看股票者”是双向耦合的,如果观察者中还有想看NBA直播、看电影的,那“前台”类就得改动。这违反了开放-封闭原则,也违背了依赖倒转原则,我们应该让程序都依赖抽象,而不是互相依赖。我们发现,老板也好,前台也好,秘书也好,都是具体的通知者。这里观察者也不应该依赖具体的实现,而是一个抽象的通知者。

第二版,解耦实践:

//通知者接口
public interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
    String getSubjectState();
    void setSubjectState(String action);
}
public class Boss implements Subject{
    private List<Observer> observers = new ArrayList<Observer>();
    private String action;
    @Override
    public void attach(Observer observer) {
        observers.add(observer);
        
    }
    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
        
    }
    @Override
    public void notifyObservers() {
        for(Observer o : observers){
            o.update();
        }
        
    }
    @Override
    public String getSubjectState() {
        return this.getAction();
    }
    public String getAction() {
        return action;
    }
    public void setAction(String action) {
        this.action = action;
    }
    @Override
    public void setSubjectState(String action) {
        this.setAction(action);
        
    }
}

前台类与老板类类似,略。

public abstract class Observer {
    protected String name;
    protected Subject sub;
    public Observer(String name, Subject sub){
        this.name = name;
        this.sub = sub;
    }
    public abstract void update();
}
public class StockObserver1 extends Observer{
    public StockObserver1(String name, Subject sub){
        super(name, sub);
    }
    public void update(){
        System.out.println(sub.getSubjectState() + " " + name + " 关闭股票行情,继续工作!");
    }
}
public class NBAObserver extends Observer{
    public NBAObserver(String name, Subject sub){
        super(name, sub);
    }
    public void update(){
        System.out.println(sub.getSubjectState() + " " + name + " 关闭NBA直播,继续工作!");
    }
}
public class Test2 {

    public static void main(String[] args) {
        //前台人员
        Subject boss = new Boss();
        //看股票的同事
        Observer yyy = new StockObserver1("xiaoming", boss);
        Observer zzz = new NBAObserver("xiaohua", boss);
        
        //前台记下两位同事
        boss.attach(zzz);
        boss.attach(yyy);
        boss.detach(yyy);
        boss.setSubjectState("你们的boss我回来了");
        boss.notifyObservers();
    }

}

这就是改进版的观察者模式。

观察者模式:也叫做发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有的观察者,使他们能够自动地更新自己。

观察者模式特点:

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是要维护相关对象间的一致性。这个给维护、扩展、重用都带来了不便。而观察者模式解决了这个不便。那什么时候该用此模式呢?当一个对象的改变需要同时改变其他对象的时候。总的来讲,观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体,从而使得各自的变化都不会影响到另一边的变化。该实现方法也有不足之处,尽管已经使用了依赖倒转原则,但“抽象通知者”还是依赖“抽象观察者”,也就是说万一没了抽象观察者这样的接口,这通知功能就完不成了。另外就是每个具体的观察者,它不一定是“更新”的方法要调用,而是其他不同名的方法。

第三版,事件委托实现

public class Event {
    //要执行方法的对象
    private Object object;
    //要执行的方法名称
    private String methodName;
    //要执行方法的参数
    private Object[] params;
    //要执行方法的参数类型
    private Class[] paramTypes;
    public Event(){
        
    }
    public Event(Object object, String methodName, Object...args){
        this.object = object;
        this.methodName = methodName;
        this.params = args;
        contractParamTypes(this.params);
    }
    //根据参数数组生成参数类型数组
    private void contractParamTypes(Object[] params){
        this.paramTypes = new Class[params.length];
        for(int i = 0; i < params.length; i++){
            this.paramTypes[i] = params[i].getClass();
        }
    }
    public Object getObject() {
        return object;
    }
    public void setObject(Object object) {
        this.object = object;
    }
    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public Object[] getParams() {
        return params;
    }
    public void setParams(Object[] params) {
        this.params = params;
    }
    public Class[] getParamTypes() {
        return paramTypes;
    }
    public void setParamTypes(Class[] paramTypes) {
        this.paramTypes = paramTypes;
    }
    //执行该对象的该方法
    public void invoke() throws Exception{
        String name = this.getMethodName();
        Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
        if(null == method){
            return;
        }
        method.invoke(this.getObject(), this.getParams());
    }
}
public class EventHandler {
    private List<Event> objects;
    public EventHandler(){
        objects = new ArrayList<Event>();
    }
    //添加某个对象要执行的事件及需要的参数
    public void addEvent(Object object, String methodName, Object...args){
        objects.add(new Event(object, methodName, args));
    }
    //通知所有的对象执行指定的事件
    public void notifyX() throws Exception{
        for(Event e : objects){
            e.invoke();
        }
    }
}
public abstract class Notifier {
    private EventHandler eventHandler = new EventHandler();
    //增加需要帮忙放哨的学生
    public abstract void addListener(Object object, String methodName, Object...args);
    public abstract void notifyX();
    public EventHandler getEventHandler() {
        return eventHandler;
    }
    public void setEventHandler(EventHandler eventHandler) {
        this.eventHandler = eventHandler;
    }
}
public class GoodNotifier extends Notifier{

    @Override
    public void addListener(Object object, String methodName, Object... args) {
        System.out.println("有新的同学委托尽职尽责的放哨人");
        this.getEventHandler().addEvent(object, methodName, args);    
    }

    @Override
    public void notifyX() {
        System.out.println("尽职尽责的放哨人告诉所有需要帮忙的同学:老师来了");
        try {
            this.getEventHandler().notifyX();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
public class PlayingGameListener {
    public PlayingGameListener(){
        System.out.println("我正在玩游戏 开始时间:" + new Date());
    }
    public void stopPlayingGame(Date date){
        System.out.println("老师来了,快回到座位上,结束时间:" + date);
    }
}
public class WatchingTVListener {
    public WatchingTVListener(){
        System.out.println("我正在看电视开始时间:" + new Date());
    }
    public void stopWatchingTV(Date date){
        System.out.println("老师来了,快关闭电视,结束时间:" + date);
    }
}
public class Test {
    public static void main(String[] args){
        Notifier goodNotifier = new GoodNotifier();
        PlayingGameListener playingGameListener = new PlayingGameListener();
        WatchingTVListener watchingTVListener = new WatchingTVListener();
        goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
        goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        goodNotifier.notifyX();
    }
}

现在可以解释一下委托是什么了。委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法可以像其他任何方法一样,具有参数和返回值,委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。new EventHandler()其实就是创建了一个委托实例,一个委托可以搭载多个方法,所有方法被依次唤起,它可以使委托对象所搭载的方法并不需要属于同一个类。但委托也是有前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。

posted @ 2018-07-20 15:19  Ericyshi  阅读(585)  评论(0编辑  收藏  举报