欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式之观察者模式

程序员文章站 2022-06-19 10:43:31
工作中我们经常会出现这样一种场景:在工作不忙的时候,老板出去办事了,然后很多员工就在工位上开始忙自己的事,比如看股票,看视频,看小说,吃零食。前台员工充当哨兵,只要老板一来,就马上通知员工们。 第一版,双向耦合代码实现: 我们发现“前台”类与“看股票者”是双向耦合的,如果观察者中还有想看NBA直播、 ......

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

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

//秘书充当前哨
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()其实就是创建了一个委托实例,一个委托可以搭载多个方法,所有方法被依次唤起,它可以使委托对象所搭载的方法并不需要属于同一个类。但委托也是有前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。