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

  • 设计模式解析一 工厂模式的不同
  • 设计模式解析二 结构模式三剑客
  • 设计模式解析三 行为模式三剑客
  • 设计模式解析四 模板方法模式和外观模式
  • 设计模式解析五 观察者模式和桥接模式
  • 设计模式解析六 单例模式

一. 前言

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

二. 观察者模式

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

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> observers = new ConcurrentHashMap<>();
    @Override
    public void subscribe(Observer observer, MagazineType type) {
        List 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 list = observers.getOrDefault(type, new ArrayList<>());
        list.remove(observer);
    }
    @Override
    public void publish(MagazineType type, String book) {
        List 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 {

    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.
     * 

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. *

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. *

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个类就行了,而就算任意增加画笔材质,或者增加颜色,也只增加一个类。
所以现实中我们知道那些画画的人总是有画笔,还有额外的颜料。
而这种把抽象进行分离解耦的方式就是桥接模式。

定义

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

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

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

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

你可能感兴趣的:(设计模式解析五 观察者模式和桥接模式)