美文网首页
设计模式解析五 观察者模式和桥接模式

设计模式解析五 观察者模式和桥接模式

作者: Cloud_Leung | 来源:发表于2019-03-14 22:17 被阅读0次

    一. 前言

    第五篇要讲的也是一个行为型模式,观察者模式,话不多说,直接开始吧。

    二. 观察者模式

    不知道大家有没有向出版社订阅过杂志的经历,我小时候定过,隐约记得拿本书叫《青年文摘》。
    那么出版社有这么多杂志类型,有娱乐杂志、有经济杂志、有科技杂志,我们订阅的时候选择其中一种或几种来订阅,杂志出版的时候就会像所有订阅了杂志的人邮寄杂志,事实上这种出版+订阅的模式就是观察者模式。
    我们来实现代码,首先我们定一个观察者,也就是我们这些要订书的订阅者们的接口:

    public interface Observer {
        void post(String book);
    }
    

    再定义个杂志社:

    public enum MagazineType {
        // 娱乐杂志
        ENTERTAINMENT,
        // 科技杂志
        SCIENCE
    }
    public interface PeriodicalOffice {
        /**
         * 订阅杂志
         */
        void subscribe(Observer observer,MagazineType type);
        /**
         * 取消订阅
         */
        void unSubscribe(Observer observer,MagazineType type);
        /**
         * 出版杂志,出版时会向所有订阅者邮寄杂志
         */
        void publish(MagazineType type, String book);
    }
    

    让我们开一家星星杂志社好了:

    public class StarPeriodicalOffice implements PeriodicalOffice {
        // 存储所有订阅者
        private static final Map<MagazineType, List<Observer>> observers = new ConcurrentHashMap<>();
        @Override
        public void subscribe(Observer observer, MagazineType type) {
            List<Observer> list = observers.getOrDefault(type, new ArrayList<>());
            if (!list.contains(observer)) {
                list.add(observer);
                observers.put(type, list);
            }
        }
        @Override
        public void unSubscribe(Observer observer, MagazineType type) {
            List<Observer> list = observers.getOrDefault(type, new ArrayList<>());
            list.remove(observer);
        }
        @Override
        public void publish(MagazineType type, String book) {
            List<Observer> list = observers.getOrDefault(type, new ArrayList<>());
            list.forEach(o -> o.post(book));
        }
    }
    

    杰克和萝丝来订阅杂志了:

    public class Jack implements Observer {
        @Override
        public void post(String book) {
            System.out.println("杰克收到了杂志社邮寄来的书:" + book);
        }
    }
    public class Rose implements Observer {
        @Override
        public void post(String book) {
            System.out.println("萝丝收到了杂志社邮寄来的书:" + book);
        }
    }
    

    接下来是杂志订阅和发布的过程:

        public static void main(String[] args) {
            PeriodicalOffice office = new StarPeriodicalOffice();
            Observer jack = new Jack();
            Observer rose = new Rose();
            office.subscribe(jack, MagazineType.ENTERTAINMENT);
            office.subscribe(jack, MagazineType.SCIENCE);
            office.subscribe(rose, MagazineType.ENTERTAINMENT);
    
            office.publish(MagazineType.SCIENCE, "科学故事");
            office.publish(MagazineType.ENTERTAINMENT, "娱乐圈的故事");
        }
    

    执行结果:

    杰克收到了杂志社邮寄来的书:科学故事
    杰克收到了杂志社邮寄来的书:娱乐圈的故事
    萝丝收到了杂志社邮寄来的书:娱乐圈的故事
    

    这就是观察者模式,观察者模式在spring框架中也有运用。
    Spring的事件监听机制就是观察者模式的实现:

    @Configuration
    public class AppEvent implements ApplicationListener<ApplicationEvent> {
    
        private static final LoggerAdapter LOGGER = LoggerFactory.getLogger(AppEvent.class);
        @Autowired
        private Environment environment;
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ApplicationReadyEvent) {
                LOGGER.info("应用已启动, 当前运行的端口是: {} ", environment.getProperty("server.port"));
                return;
            }
            if (event instanceof ApplicationFailedEvent) {
                LOGGER.info("应用启动失败");
            }
        }
    }
    

    这里的监听是对所有应用ApplicationEvent事件监听,当然泛型也可以选择针对某个之间进行监听,比如只监听ApplicationReadyEvent

    public interface ApplicationEventMulticaster {
    
        /**
         * Add a listener to be notified of all events.
         * @param listener the listener to add
         */
        void addApplicationListener(ApplicationListener<?> listener);
    
        /**
         * Add a listener bean to be notified of all events.
         * @param listenerBeanName the name of the listener bean to add
         */
        void addApplicationListenerBean(String listenerBeanName);
    
        /**
         * Remove a listener from the notification list.
         * @param listener the listener to remove
         */
        void removeApplicationListener(ApplicationListener<?> listener);
    
        /**
         * Remove a listener bean from the notification list.
         * @param listenerBeanName the name of the listener bean to add
         */
        void removeApplicationListenerBean(String listenerBeanName);
    
        /**
         * Remove all listeners registered with this multicaster.
         * <p>After a remove call, the multicaster will perform no action
         * on event notification until new listeners are being registered.
         */
        void removeAllListeners();
    
        /**
         * Multicast the given application event to appropriate listeners.
         * <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
         * if possible as it provides a better support for generics-based events.
         * @param event the event to multicast
         */
        void multicastEvent(ApplicationEvent event);
    
        /**
         * Multicast the given application event to appropriate listeners.
         * <p>If the {@code eventType} is {@code null}, a default type is built
         * based on the {@code event} instance.
         * @param event the event to multicast
         * @param eventType the type of event (can be null)
         * @since 4.2
         */
        void multicastEvent(ApplicationEvent event, ResolvableType eventType);
    
    }
    

    ApplicationEventMulticaster类就是所有的观察者管理类,所有监听器都会通过这个接口来进行注册,遇到应用的事件发生时也通过该接口来进行通知。至于是怎么注册进来的,就是在spring 容器加载bean时候判断我们的bean是否是ApplicationListener的实现类,如果是就帮我们注册进来,有兴趣的可以去看看源码。

    实际上Spring的源码可以说是集设计模式大成者,我了解的也是百不足一,其中很多源码都值得我们去研究学习,不能因为它为我们提供了简便的使用方式,我们就不去了解学习了。

    定义

    观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

    三. 桥接模式

    桥接模式是一个很难理解的模式,夜也深了,这个模式会用例子来解释,但就不写代码了。

    小时候大家一定学过画画,我记得小时候有一种一套的画笔,里面会包含一二十种画笔,每种颜色不同,小时候没觉得什么,但是从程序猿的角度再来思考一下这个东西。

    • 画笔可以有很多种类 软毛、硬毛
    • 画笔还会有多种颜色 赤橙黄绿青蓝紫

    现在让我们从程序的角度抽象一下画家的画笔:画笔的材质、画笔颜色
    有这两种可变特性,设计接口的话,是不是接口就应该有获取这两个属性的方法,接下来,根据这两种特性进行组合,然后编写实现类的话,总共有14支笔,要写14个类,what??如果再加一种毛的材料,我是不是还得再加7个类,这绝对不可能。
    那么怎么办?我们是不是可以把这两种抽象进行分离呢?

    • 定义一个画笔毛刷种类的接口
    • 定义一个颜色接口

    然后再使用画笔的时候在对这两种属性进行画笔的组合,是不是我们就可以做到只有9个类就行了,而就算任意增加画笔材质,或者增加颜色,也只增加一个类。
    所以现实中我们知道那些画画的人总是有画笔,还有额外的颜料。
    而这种把抽象进行分离解耦的方式就是桥接模式。

    定义

    桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化。

    这句话以前一直很不容易理解,现在可以试着理解一下了,说回上面的画笔,其中画笔材质,还有画笔颜色都是画笔的抽象,通常来讲会将这两个抽象定义到一个接口里,但由于每一种抽象有可能会分开变化,所以我们将其中的部分抽象,比如颜色,分离出去,然后通过类的组合关联的方式让画笔具有颜色的属性,这就是抽象部分和实现部分进行分离开来的意思。

    套用《大话设计模式》里面的就是实现系统可能有多个角度分类,每一种角度都可能变化,那么把这种多角度分类给分离出来让他们独立变化,减少他们之间耦合。

    桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。

    相关文章

      网友评论

          本文标题:设计模式解析五 观察者模式和桥接模式

          本文链接:https://www.haomeiwen.com/subject/xxehmqtx.html