读《图解设计模式》的所思所想

困惑

做者试图从另外一个角度阐述设计模式,因此对 23 种具体设计模式进行了从新分类,但整本书读下来比较困惑,在于几点:html

  1. 分类标准不统一,有实现思路、实现内容、模式目的等标准,甚至还有“适应设计模式”这种分类,很有些无从分类的“自暴自弃”的味道。同时在这种分类方式下,还存在一个问题,即某设计模式的实现是会用到另外一个设计模式的,甚至其些设计模式的书中实现类图会基本相同,可是却属于不一样分类,带来了新的困惑,好像要强迫你在学习一个设计模式时,要忘掉其余设计模式的存在。
  2. 缺少对具体设计模式适用场景的充分阐述,知何殊不知为什么。
  3. 做为入门书,未对更低层的原则进行科普,即便知道了各具体模式能够达成哪些具体目的,却没法融汇到统一的思想出口。
  4. ?

可是总以为还有一个抓不到的缘由,那么再深刻探究一下,究竟是什么令我产生困惑呢?这就须要了解设计模式的起源。编程

根源

设计模式(design pattern)是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。它描述了在软件设计过程当中的一些不断重复发生的问题,以及该问题的解决方案。设计模式

设计模式是问题的方案。数据结构

设计模式是经验的总结。架构

首先,正确的学习方式应该是带着问题找答案。若是答案被直接摆在面前殊不知问题,不管是谁都会产生“这是啥?!”的困惑。但更重要的是,经验是具备普适性的,具体的设计模式其实体现的是具体的思想方式,这种思想方式与语言无关,同时在单一语言中也必定有多种实现形式,那么此时就进入到了抽象和具象的冲突。若无顿悟的天分,接收到思想的抽象概念描述时,会有一种脑子懂了,却无从下手的感受,同时若以书中有限的应用示例来描述,又没法彻底体会到思想的方方面面。函数

因此设计模式的学习应该是快速的阅读书籍,在对模式有轮廓性认识后,带着问题,不断实践练习的一个过程,要在实践中得出本身的体会,将从书中获得的融到本身的骨子里。这也是形成前面讲的困惑的根本缘由了,实践不够呀。学习

这其中还有另外一个教训,我曾经陷入了为何能用这种设计模式而不能用另外一种设计模式的思惟旋涡,同样,只靠想,不依托实践,这些问题是解决不了的。因此不要把时间浪费在纠结的思考中。.net

也是所以,后续内容不会是面面俱到的长篇累牍,只会对设计模式的脉络作基于目的的简要阐述。设计

目的与手段

维护一个软件的长期良性发展是究极目标,即提升可维护性,下降维护成本。能够从抽象等级分为 4 个层次。代理

  1. 目标:维护性。
  2. 标准:扩展性、重用性、高内聚、低耦合。
  3. 原则:7 大基本原则。
  4. 模式:23 + N 种设计模式。

应该经过提高扩展性、重用性等达到高内聚、低耦合的特性。

在这个过程当中应遵循 7 大原则,同时这些原则又是设计模式的基础,是设计模式为什么如此设计的依据。

而模式则是更具体的思想范式,设计模式不只仅局限于 23 种,跟随技术水平的发展,也伴生出了新的问题,也就总结出了针对新问题的 N 种模式。

7 大基本原则

设计模式每每是基于类,接口来说的,而 JS 并不是基于类的语言,支持度不够,同时咱们又不该该将模式的思想拘泥于类中,因此能够将下述原则的应用个体,如类、接口,放到函数或模块等其余维度上体会。

  • 单一职责原则:

    • 单一职责原则规定一个类应该有且仅有一个引发它变化的缘由,不然类应该被拆分。
    • 咱们没必要要拘泥于类,该原则的根本目的是控制职责所在的个体复杂度。只须要明白单一个体只须要作好一件事,个体越简单则可读性越好,职责划分越明确,则改动发生时,越不会影响其余个体。
    • 好比这种职责拆分能够发生在函数粒度,也能够发生在函数的聚合层面(类或者更外层的函数),职责和个体理想状态下应该是一对一的。
    • 这个原则要求咱们能清晰的认识到代码逻辑中的多重职责,从而才能进行划分。
  • 接口隔离原则:

    • 客户端不该该被迫依赖于它不使用的方法。一个类对另外一个类的依赖应该创建在最小的接口上。
    • 即对于依赖者,被依赖者应该只提供他关心的功能。当体如今接口上时,就是接口隔离原则,将有冗余的接口拆分。
    • 能够避免因为依赖者的增多致使接口膨胀,影响到其余的依赖者。
    • 相对于单一职责原则能够理解为单一职责原则是对内作最少承诺,而接口隔离原则是对外作最少的承诺。
  • 依赖倒置原则:

    • 高层模块不该该依赖低层模块,二者都应该依赖其抽象;抽象不该该依赖细节,细节应该依赖抽象。
    • 即面向接口编程。咱们只须要对低层进行接口定义,高层只须要关注有哪些接口并进行调用,低层实现时只要实现了这些接口,那么传给高层的实例发生变化时,高层就不须要修改。下降了耦合度。
  • 开闭原则:

    • 软件实体应当对扩展开放,对修改关闭。
    • 在软件修改时,尽可能经过扩展而实现而不是经过修改来实现,避免对现有逻辑的影响。
  • 合成复用原则:

    • 在复用时,要尽可能先使用组合(实例化是就存在)或者聚合(经过 API 调用添加为成员变量)等关联关系来实现,其次才考虑使用继承关系来实现。
    • 继承强耦合,组合聚合是弱耦合。
  • 里氏替换原则:

    • 继承必须确保超类所拥有的性质在子类中仍然成立。即子类能够扩展父类的功能,但不能改变父类原有的功能。
  • 迪米特法则:

    • 只与你的直接朋友交谈,不跟“陌生人”说话,又叫最少知识原则。即一个类对本身依赖的类知道的越少越好。
    • 耦合是没法彻底避免的。
    • 被依赖的类不论多复杂,都应该将细节封装在内部,对外暴露 API。
    • 应该避免类中出现非直接的朋友关系(直接朋友关系:成员变量、参数、返回值)的依赖。

能够看出这些原则都是为了个体间的低耦合而努力。

模式的一句话描述

  1. 迭代器模式:为了在不暴露数据的内部结构的前提下对外提供可替换的迭代方式。此模式隐藏内部细节,且可替换迭代方式。这种思路可推广至迭代之外的其余能力。
  2. 适配器模式:为了使不兼容的接口协同工做,将现有接口包装为须要的接口。在处理代码边界即第三方依赖时也可以使用,能在第三方依赖被换掉时下降改动成本。
  3. 模板方法模式:在流程结构肯定,而步骤的具体实现不定或有差别时使用。即定义好模板,但将具体处理的实现交给子类,扩展新的能力时只需实现新的子类。
  4. 工厂方法模式:建立接口不变的状况下,由用户决定什么哪一个实例时,使用。产品和工厂一一对应,扩展新的产品时须要增长新的产品类和对应的工厂类。
  5. 单例模式:单一类只容许生成一个实例时使用。
  6. 原型模式:避免较高的实例化成本时使用。经过复制生成实例,核心在于复制现有实例,避免重走实例化的过程。
  7. 建造者模式:最终产出物的组成部分相同,但须要组装过程可替换时使用。经过组装生成复杂实例,并将组装过程抽离至独立的类,核心在于侧重组装,那么实现不一样的组装过程类就能产出不一样的产出物。
  8. 抽象工厂模式:与工厂方法模式相似,当工厂类产出多个产品时可使用抽象工厂模式。注意区别是是工厂产出一个仍是多个产品。
  9. 桥接模式:在对外提供的功能接口内有多个维度的变化时使用,将类的对外接口和实现分为独立的两个类,对外接口经过在内部组合使用实现类来完成具体实现,可减小维度引发的类数量的爆炸增加。
  10. 策略模式:当咱们完成任务的策略须要可被替换时使用。将经过策略完成任务的过程拆分为调用和实现,实现部分提供成系统的方法簇,即具体策略,以参数形式传给调用部分,从而实现策略可替换。
  11. 组合模式:当须要提供给用户的是多个对象,且对象间是部分-总体的层次结构,且不但愿用户关心对象间差别,只要一套访问接口时使用。经过多个子类实现相同接口实现。
  12. 装饰器模式:给一个对象追加更多功能,且不改变提供给用户的接口时使用。
  13. 访问者模式:在不改变数据结构的前提下能够添加对数据结构的新的操做时使用。经过将数据结构与操做分离的方式实现。
  14. 责任链模式:个体须要被多个对象处理,但处理对象间有没有耦合关系时,为了不增长系统复杂度时使用。经过将多个处理对象组成一条责任链,而后将待处理目标沿着这条链传递进行处理实现。Koa 中间件机制就是一种实践。
  15. 门面模式:简化用户对复杂系统中子系统的联系,对外提供简单易用的接口时使用。经过包装更高层的类,由它调度子系统实现。
  16. 中介者模式:简化复杂系统中子系统之间的联系,将交互封装一个中介对象,下降子系统对象间的耦合。能够体会下与门面模式的不一样。
  17. 观察者模式:一个对象改变时须要致使其余对象也改变,且不关心其余对象具体是谁时可使用。经过观察对象管理监听他的全部观察者,并在发生变化时通知全部观察者实现。
  18. 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态,以便之后当须要时能将该对象恢复到原先保存的状态时使用。
  19. 状态模式:对象与外部互动致使状态变化,从而行为不一样时使用。经过将不一样状态下的行为封装为不一样的类,容许在状态改变时经过切换状态类改变行为实现。能够理解为策略模式的一种特殊应用。是一种内置了多种“策略”,根据状态变化切换“策略”的模式。
  20. 享元模式:有大量的实例须要公用或重复使用时使用。咱们能够把这些实例当作享元,并管理起来。能够当作同时使用了多个单例模式的类,由于存在管理能力,因此会受外部因素影响,在返回前修改单例的状态。
  21. 代理模式:在咱们不想让用户直接使用对象的状况下使用,如加以访问控制。很简单,实现方式就是加一层代理来间接引用对象。
  22. 命令模式:须要使命令发起者和执行者不可见,甚至须要对命令加以管理时使用。此模式经过将命令封装为一个类,将命令执行者做为命令的依赖,分离命令调用者和命令实现者,同时因为命令实例的存在又能够对命令加以管理。
  23. 解释器模式:将发生频率足够高的问题的各个实例表述为一个简单语言的句子,并构建一个解释器解释语言中的句子。经过对定义语句语法节点,并针对每类语法节点声明类,对语句节点遍历解析实现。

咱们能够从上述描述中看到重复的几个关键词:拆分、不关心、不破坏,仍是在为了个体间的低耦合而努力。

并且要注意的是,模式并不是完美,有些模式实现时甚至会增长内部耦合,增长系统复杂度,因此要关注目的,关注目的,关注目的,关注是否下降了所关注的可能变化的点的耦合度。

结语

最后以三个问题结束这篇文章。

学什么?

咱们学设计模式,是为了学习如何合理的组织咱们的代码,如何解耦,如何真正的达到对修改封闭对扩展开放的效果,而不是去背诵那些类的继承模式,而后本身记不住,回过头来就骂设计模式把你的代码搞复杂了,要反设计模式。

如何用?

为了合理的利用设计模式,咱们应该明白一个概念,叫作扩展点。扩展点不是天生就有的,而是设计出来的。咱们设计一个软件的架构的时候,咱们也要同时设计一下哪些地方之后能够改,哪些地方之后不能改。

如何用的好?

“我亦无他,唯手熟尔。”

参考