换个角度理解之设计模式

2019-02-26  本文已影响12人  夫礼者

这里以经典的二十三种来进行讨论。

概述

其实这个主题在本系列创建之初就建好了,但自感对于设计模式的理解还处于小学生阶段,故一直拖延;但时隔一年有余,发觉在理解上并没有实质性的进展,遂决定公开。希望能与诸位有一个互动,有所突破。

首先让我们回顾下支撑本系列的两个核心观点——"知道自己不该知道什么","推迟做出决定的时机",想要让这两个观点和二十三种经典设计模式全部直接相关实在有点牵强附会,因此笔者将紧着适度的原则,只讨论那些与两个核心观点有着直接关系的。

个人理解

广为我们所熟悉的分类方法将设计模式大体上分为三大类:

  1. 创建型模式。共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  2. 结构型模式。共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  3. 行为型模式。共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
    其他诸如并发型模式和线程池模式,这里不纳入讨论范围了,接下来我们就按照这三大类的顺序进行讨论。
创建型模式

这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

  1. 工厂模式与抽象工厂模式 (Factory Pattern)(Abstract Factory Pattern):不同条件下创建不同实例。
  2. 单例模式 (Singleton Pattern):保证一个类仅有一个实例。
  3. 建造者模式 (Builder Pattern):将一个复杂的构建过程与其具表示细节相分离,使得同样的构建过程可以创建不同的表示。
  4. 原型模式 (Prototype Pattern):通过拷贝原型创建新的对象。

这个可以统一用“自己不该知道什么”来解释就完全解释得通了,在平时的开发中,我们所构造的一系列对象并不是我们的目的,拿着它们去完成我们的目标才是我们的目的。所以我们并不关心这些对象内部构造,而只是希望在我们真正需要时,可以完成我们要求的对象已经构建好了。Spring正是基于这样简单的理念构造出了一个庞大的帝国。

创建型模式的不同划分主要是基于不同的时机需求,但中心思想都是一致的:

  1. 我不关心你在什么时候,采用怎么样的方式构建出怎么样的实体(我要的是一个敲钉子的物品,砖头、瑞士军刀、锤头你给哪个都行)。SICP使用"cloud"来代指这一现象,我们最终拿到的是一个 cloud, 其内部的细节我们完全不需要关心,而仅仅只关注于其提供的API是否满足我们的要求。
  2. 对于构建型模式,因为职责的拆分,构建者们就可以按照实际的需求自主决定实际构建出该实例的时机,这也就契合了"推迟做出决定的时机",因为我并没有实际做出决定,所以未来我所做的决定不会被当前所做出的构建实例的决定所约束,束缚住手脚,这就是所谓的灵活性。
  3. 这组模式是两个核心观念能解释得最完美的。
结构型模式

这些设计模式关注类和对象的组合。

  1. 适配器模式 (Adapter Pattern):使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  2. 装饰器模式 (Decorator Pattern):保持接口,增强性能:修饰类继承被修饰对象的抽象父类,依赖被修饰对象的实例(被修饰对象依赖注入),以实现接口扩展。
  3. 代理模式 (Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问:增加中间层(代理层),代理类与底层实现类实现共同接口,并创建底层实现类对象(底层实现类对象依赖注入代理类),以便向外界提供功能接口。
  4. 组合模式 (Composite Pattern):用户对单个对象和组合对象的使用具有一致性的统一接口。
  5. 外观模式 (Facade Pattern):在客户端和复杂系统之间再加一层,这一次将调用顺序、依赖关系等处理好。即封装底层实现,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的高层接口。
  6. 桥接模式 (Bridge Pattern):两个维度独立变化,依赖方式实现抽象与实现分离:需要一个作为桥接的接口/抽象类,多个角度的实现类依赖注入到抽象类,使它们在抽象层建立一个关联关系。
  7. 享元模式 (Flyweight Pattern):享元工厂类控制;HashMap实现缓冲池重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

以上这些模式中,在Java代码的实现中,绝大部分都是使用接口来作为媒介的。我们抛开Java8引入的default method,接口的最大特点就是其中不包含任何实现代码,这也正应了我们所说的,因为我们知道自己不该知道什么,所以我们将我们需要知道的定义为接口,然后从某个位置(例如Spring容器)中取出该借该接口的实现类。以上操作中,对于我而言,只需要知道的是该接口所宣称能完成的功能。

现在让我们回到结构型模式,诸如适配,装饰组合等都是为了在确保客户端对接口的认知不变的前提下,加入服务提供者自身需要的功能。服务的提供者需要时刻谨记"客户端不应该知道的"来向上提供接口实现,也要秉承该该原则去调用自身依赖的接口。

行为型模式

这些设计模式特别关注对象之间的通信。

  1. 责任链模式(Chain of Responsibility Pattern):拦截的类都实现统一接口,每个接收者都包含对下一个接收者的引用。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
  2. 观察者模式(Observer Pattern):一对多的依赖关系,在观察目标类里有一个 ArrayList 存放观察者们。当观察目标对象的状态发生改变,所有依赖于它的观察者都将得到通知,使这些观察者能够自动更新(即使用推送方式)
  3. 模板模式(Template Pattern):将这些通用算法抽象出来,在一个抽象类中公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
  4. 命令模式(Command Pattern):将"行为请求者"与"行为实现者"解耦:调用者依赖命令,命令依赖接收者,调用者Invoker→命令Command→接收者Receiver。
  5. 解释器模式(Interpreter Pattern):给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
  6. 迭代器模式(Iterator Pattern):集合中含有迭代器:分离了集合对象的遍历行为,抽象出一个迭代器类来负责,无须暴露该对象的内部表示。
  7. 中介者模式(Mediator Pattern):对象与对象之间存在大量的关联关系,将对象之间的通信关联关系封装到一个中介类中单独处理,从而使其耦合松散,可以独立地改变它们之间的交互。
  8. 策略模式(Strategy Pattern):策略对象依赖注入到context对象,context对象根据它的策略改变而改变它的相关行为(可通过调用内部的策略对象实现相应的具体策略行为)。
  9. 状态模式(State Pattern):状态对象依赖注入到context对象,context对象根据它的状态改变而改变它的相关行为(可通过调用内部的状态对象实现相应的具体行为)。
  10. 备忘录模式(Memento Pattern):通过一个备忘录类专门存储对象状态。客户通过备忘录管理类管理备忘录类。
  11. 空对象模式(Null Object Pattern):创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。不要为了屏蔽null而使用空对象,应保持用null,远比用非null的值来替代“无值”要好。

以上模式诸如职责链和观察者,都是只处理自己所关注的那一部分信息,然后将控制权交回给框架,对自己不关心的内容直接无视。

最后

诸多设计模式,当你换个思考角度,比如站在使用者的角度去思考所遇到问题的时候,相信你对设计模式的应用场景会清晰不少。比如适配器,我们在做老系统升级时候就经常需要该模式来完成类似给高速运行的动车换发动机的操作。

上一篇下一篇

猜你喜欢

热点阅读