zoukankan      html  css  js  c++  java
  • 设计模式之观察者模式

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

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

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

  • 相关阅读:
    Web 自动化测试(Selenium) PO 模型
    Web 自动化测试(Selenium) 鼠标和键盘操作以及浏览器等待
    Web 自动化测试(Selenium)进阶及八大元素定位
    web 自动化测试(Selenium) Xpath 和 Css 定位元素
    没有最全,只有更全的正则表达式集合(持续更新...)
    SQL优化第一篇
    C# 设置桌面为父窗口
    Spring Boot整合MybatisPlus逆向工程(MySQL/PostgreSQL)
    IDEA2020.2版本设置类和方法的自定义注释模板
    记一个Java多线程相关的面试题
  • 原文地址:https://www.cnblogs.com/shicaiyou/p/9341653.html
Copyright © 2011-2022 走看看