10、观察者模式—查理大帝更衣,反射实现事件委托

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

UML图:



简单实现:

package com.thpin.repository.designpattern;

import com.thpin.repository.collection.util.ArrayList;
import com.thpin.repository.collection.util.List;

public class ObserverDemo {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        subject.attach(new ConcreteObserver(subject));
        subject.attach(new ConcreteObserver(subject));
        subject.attach(new ConcreteObserver(subject));
        subject.notifyObserver();
    }
}

/*
 * 抽象通知者
 */
abstract class Subject {

    private List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObserver() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

/*
 * 抽象观察者
 */
abstract class Observer {
    private Subject subject;

    public Observer(Subject subject) {
        this.subject = subject;
    }

    public Subject getSubject() {
        return subject;
    }

    public void setSubject(Subject subject) {
        this.subject = subject;
    }

    public abstract void update();
}

/*
 * 具体通知者
 */
class ConcreteSubject extends Subject {
    private String action;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }
}

/*
 * 具体观察者
 */
class ConcreteObserver extends Observer {

    public ConcreteObserver(Subject subject) {
        super(subject);
    }

    public void update() {
        System.out.println("通知已收到");
    }
}

结果:

通知已收到
通知已收到

通知已收到


其实jdk中已经为我们定义好了主题和观察者接口,Observable(具体类)和Observer(接口),它们的源码希望自己去看一下,这里就不列出来了,Observable实现了同步功能,Observer只有一个update()方法。注意观察者列表执行的顺序是倒序。


下面我们看一个利用jdk接口实现的观察者模式,模拟给查理大帝更衣

package com.thpin.repository.designpattern;

import java.util.Observable;
import java.util.Observer;

/*
 * 这个观察者模式是利用jdk实现的
 */
public class ObserverDemo2 {
    public static void main(String[] args) {
        Publish publish = new Publish();
        publish.setEmperor(new Emperor("查理大帝"));
        new Subscribe1(publish);
        new Subscribe2(publish);
        new Subscribe3(publish);
        publish.publishCommond();

        System.out.println("废物,内裤穿外面了!!! 换人");
    }
}

/*
 * 发布
 */
class Publish extends Observable {
    private Emperor emperor;

    public Emperor getEmperor() {
        return emperor;
    }

    public void setEmperor(Emperor emperor) {
        this.emperor = emperor;
    }

    public void publishCommond() {
        System.out.println("为" + emperor.getName() + "更衣!");
        setChanged();
        notifyObservers(emperor);
    }
}

/*
 * 订阅
 */
class Subscribe1 implements Observer {
    public Subscribe1(Observable o) {
        o.addObserver(this);
    }
    public void update(Observable o, Object arg) {
        System.out.println("收到命令");
        wearUnderpants((Emperor) arg);
    }
    private void wearUnderpants(Emperor emperor) {
        emperor.setUnderpants("龙内裤");
        System.out.println(emperor);
    }
}

/*
 * 订阅
 */
class Subscribe2 implements Observer {
    public Subscribe2(Observable o) {
        o.addObserver(this);
    }
    public void update(Observable o, Object arg) {
        System.out.println("收到命令");
        wearJacket((Emperor) arg);
    }
    private void wearJacket(Emperor emperor) {
        emperor.setJacket("龙袍");
        System.out.println(emperor);
    }
}

/*
 * 订阅
 */
class Subscribe3 implements Observer {
    public Subscribe3(Observable o) {
        o.addObserver(this);
    }
    public void update(Observable o, Object arg) {
        System.out.println("收到命令");
        wearTrousers((Emperor) arg);
    }
    private void wearTrousers(Emperor emperor) {
        emperor.setTrousers("龙裤");
        System.out.println(emperor);
    }
}

class Emperor {
    private String name; // 名字
    private String underpants; // 内裤
    private String jacket; // 上衣
    private String trousers; // 裤子
    public Emperor(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUnderpants() {
        return underpants;
    }
    public void setUnderpants(String underpants) {
        this.underpants = underpants;
    }
    public String getJacket() {
        return jacket;
    }
    public void setJacket(String jacket) {
        this.jacket = jacket;
    }
    public String getTrousers() {
        return trousers;
    }
    public void setTrousers(String trousers) {
        this.trousers = trousers;
    }
    
    @Override
    public String toString() {
        return "Emperor [name=" + name + ", underpants=" + underpants + ", jacket=" + jacket + ", trousers=" + trousers
                + "]";
    }
}

结果:

为查理大帝更衣!
收到命令
Emperor [name=查理大帝, underpants=null, jacket=null, trousers=龙裤]
收到命令
Emperor [name=查理大帝, underpants=null, jacket=龙袍, trousers=龙裤]
收到命令
Emperor [name=查理大帝, underpants=龙内裤, jacket=龙袍, trousers=龙裤]

废物,内裤穿外面了!!! 换人



观察者模式很方便并且统一的完成一个对象的状态改变而同时改变多个与这个状态绑定的若干对象的状态。但仔细观察会发现一个缺点,无论自己实现的还是jdk接口都限定了观察者更新状态方法的名称,自己实现的是update(), jdk接口Observer也是updat(),然而实际工作中我们期望充当观察者的对象不一定都有一个有意义update方法。事件委托可以弥补这个缺陷,不幸的是java没有自带事件委托机制,C#据说有这个机制。别急,没有事件委托我们就用反射机制来完成事件委托的功能。

package com.thpin.repository.designpattern;

import java.lang.reflect.Method;

import com.thpin.repository.collection.util.ArrayList;
import com.thpin.repository.collection.util.List;

/*
 * 事件委托弥补观察者模式的局限性
 * 把观察者的更新状态方法生成事件来订阅发布者
 * 观察者的状态更新方法不限于update这个名称。
 */
public class ObserverDemo3 {
    public static void main(String[] args) {
        Notifier notifier = new Notifier();
        new Observer1(notifier);
        new Observer2(notifier);
        notifier.notifyObj();
    }
}

/*
 * 事件,实际就是观察者update方法的反射封装
 */
class Event {
    private Object object;
    private String methodName;
    private Object[] params;
    private Class<?>[] paramsType;

    public Event(Object object, String methodName, Object... params) {
        this(object, methodName);
        this.params = params;
        constructParamTypes(params);
    }

    public Event(Object object, String methodName) {
        this.object = object;
        this.methodName = methodName;
    }

    private void constructParamTypes(Object[] params) {
        this.paramsType = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            this.paramsType[i] = params[i].getClass();
        }
    }

    public void invoke() throws Exception {
        Method method = null;
        if (params != null && params.length > 0) {
            method = object.getClass().getMethod(methodName, paramsType);
            method.invoke(object, params);
        } else {
            method = object.getClass().getMethod(methodName);
            method.invoke(object);
        }
    }
}

/*
 * 事件操作者
 */
class EventHandler {
    private List<Event> events = new ArrayList<>();

    public void addEvent(Object object, String methodName, Object... params) {
        events.add(new Event(object, methodName, params));
    }

    public void addEvent(Object object, String methodName) {
        events.add(new Event(object, methodName));
    }

    public void notifyObj() throws Exception {
        for (Event event : events) {
            event.invoke();
        }
    }
}

/*
 * 被观察者,或者说是发布者
 */
class Notifier {
    private EventHandler handler = new EventHandler();

    public EventHandler getHandler() {
        return handler;
    }

    public void setHandler(EventHandler handler) {
        this.handler = handler;
    }

    public void attach(Object object, String methodName, Object... params) {
        handler.addEvent(object, methodName, params);
    }

    public void notifyObj() {
        try {
            handler.notifyObj();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/*
 * 观察者1,或这说是订阅者
 */
class Observer1 {
    public Observer1(Notifier notifier) {
        notifier.attach(this, "changeState1");
    }

    public void changeState1() {
        System.out.println("Observer1改变状态");
    }
}

/*
 * 观察者2
 */
class Observer2 {
    public Observer2(Notifier notifier) {
        notifier.attach(this, "changeState2");
    }

    public void changeState2() {
        System.out.println("Observer2改变状态");
    }
}
不得不说反射很强大,能够实现很多统一工作的封装。用任何list object做参数就可以生成Excel表格,封装好的线程池随意接收任务来并发执行,这都可以用反射来完成的,以后有时间在接收这两个工具。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值