• Java设计模式----观察者模式


    交通信号灯相信大家都不陌生,红灯停、绿灯行,见了黄灯等一等。交通信号灯、海边的信号塔等等,都伫立在那里,随时准备给行人/货船发送信号,以提醒行人/货船该采取相应行动了。信号灯就是发布信号的对象,行人是接收信号并采取具体行动的观察者。这一过程,在程序设计中也常常用到,即本文介绍的观察者模式。


    1.观察者模式

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

    在事件驱动的GUI开发中观察者模式十分常用。另外,著名的MVC软件设计思想也应用了观察者模式:

    • Model 业务模型,相当于观察者模式中的主题对象
    • View 视图层,相当于观察者
    • Controller 控制器,协调M和V

    当Model数据发生变化时,会通知View更新视图。所以,观察者模式又叫做模型-视图模式,或者发布-订阅模式。


    2. 代码实现

    2.1 紧耦合形式

     Observer抽象类,有三个子类:HexObserver十六进制, OctObserver八进制,BinObserver二进制,注意观察着Observer持有主题Subject的引用。

    Subject主题对象持有Observer的引用,并提供添加add观察者的方法, 当Subject状态改变时setState, 会调用execute方法,execute方法中遍历所有观察者对象并调用每个观察者对象的update方法更新,观察者对象的update方法中,通过回调主题对象的getState()方法获取主题的状态。

    这种方式Subject持有Observer引用, Observer也持有Subject的引用,就是紧耦合的实现方式。

    abstract class Observer {
        protected Subject subject;
        public abstract void update();
    }
    
    class Subject {
        private List<Observer> observers = new ArrayList<>();
        private int state;
    
        public void add(Observer o) {
            observers.add(o);
        }
    
        public int getState() {
            return state;
        }
    
        public void setState(int value) {
            this.state = value;
            execute();
        }
    
        private void execute() {
            for (Observer observer : observers) {
                observer.update();
            }
        }
    }
    
    class HexObserver extends Observer {
        public HexObserver(Subject subject) {
            this.subject = subject;
            this.subject.add(this);
        }
    
        public void update() {
            System.out.print(" " + Integer.toHexString(subject.getState()));
        }
    }
    
    class OctObserver extends Observer {
        public OctObserver(Subject subject) {
            this.subject = subject;
            this.subject.add( this );
        }
    
        public void update() {
            System.out.print(" " + Integer.toOctalString(subject.getState()));
        }
    }
    
    class BinObserver extends Observer {
        public BinObserver(Subject subject) {
            this.subject = subject;
            this.subject.add(this);
        }
    
        public void update() {
            System.out.print(" " + Integer.toBinaryString(subject.getState()));
        }
    }
    
    public class ObserverDemo {
        public static void main( String[] args ) {
            Subject sub = new Subject();
            // Client configures the number and type of Observers
            new HexObserver(sub);
            new OctObserver(sub);
            new BinObserver(sub);
            Scanner scan = new Scanner(System.in);
            for (int i = 0; i < 5; i++) {
                System.out.print("
    Enter a number: ");
                sub.setState(scan.nextInt());
            }
        }
    }

    2.2 松耦合形式

    以下代码基于jdk1.8以上,定义内部类,可以避免在Observer中定义Subject的引用。

    EventSource事件源, scanSystemIn()方法监听控制台输入,当控制台有字符串输入时,EventSource会通知Observer进行update操作。

    /**
     * 事件源
     */
    class EventSource {
        public interface Observer {
            void update(String event);
        }
    
        private final List<Observer> observers = new ArrayList<>();
    
        public void addObserver(Observer observer) {
            observers.add(observer);
        }
    
        public void scanSystemIn() {
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                notifyObservers(line);
            }
        }
    
        private void notifyObservers(String event) {
            observers.forEach(observer -> observer.update(event));
        }
    }

    调用

    public class ObserverDemo {
        public static void main(String[] args) {
            EventSource eventSource = new EventSource();
            for (int i = 0; i < 3; i++) {
                eventSource.addObserver(event -> {
                    System.out.println("Received response: " + event);
                });
            }
            eventSource.scanSystemIn();
        }
    }

    控制台输入"click",然后事件源监听到,通知Observer,Observer将消息打印出来:

    click
    Received response: click
    Received response: click
    Received response: click

    3.总结

    其实为了避免紧耦合,实际开发中往往在Subject和Observer之间加一个中间对象,这个中间对像本质上就是一个队列,当Subject状态发生变化时,这个变化信息传到中间对象队列中,由中间对象对这条消息进行处理,包装成Observer统一的一种接收形式,然后根据某种策略发给Observer。消息中间件就是这样一种实现,

    另外,注意主题对象由于持有观察着对象集合,如果观察者数量庞大时,要注意内存的占用,避免发生内存泄漏,通常,可以考虑弱引用weak reference来避免内存占用过高。

  • 相关阅读:
    centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课
    基于HTML5 WebGL实现 json工控风机叶轮旋转
    基于HTML5的WebGL实现的2D3D迷宫小游戏
    基于HTML5和WebGL的碰撞测试
    基于HTML5和WebGL的3D网络拓扑结构图
    基于 HTML5 WebGL 的 3D 网络拓扑图
    基于HTML5 Canvas 实现弹出框
    基于HTML5 Canvas实现用户交互
    基于HTML5快速搭建TP-LINK电信拓扑设备面板
    HTML5 技术在风电、光伏等新能源领域的应用
  • 原文地址:https://www.cnblogs.com/yxlaisj/p/10539000.html
Copyright © 2020-2023  润新知