设计模式笔记

大话设计模式笔记

总结

  • 面对对象

    • (可维护) 改动时只改需要改动的地方
    • (可复用)
    • (可扩展) 以后加入需求不需要改动原来的代码
    • (灵活性) 功能可以满足很多情况
  • 重复 = 易错 + 难改

设计模式

设计模式之间的关系

  • 创建型 (Gof Creational Patterns)

    • Factory Method(工厂方法)
    • Abstract Factory(抽象工厂)
    • Builder(建造者)
    • Prototype(原型)
    • Singleton(单例)
  • 结构型 (Gof Structural Patterns)

    • Adapter Class/Object (适配器)
    • Bridge (桥接)
    • Composite (组合)
    • Decorator (装饰)
    • Facade (外观)
    • Flyweight (享元)
    • Proxy (代理)
  • 行为型 (Gof Behavioral Patterns)

    • Interpreter (解释器)
    • Template Method (模板方法)
    • Chain of Responsibility (职责链)
    • Command (命令)
    • Iterator (迭代器)
    • Mediator (中介者)
    • Memento (备忘录)
    • Observer (观察者)
    • State (状态)
    • Strategy (策略)
    • Visitor (访问者)

创建型 (Gof Creational Patterns)

工厂方法(Factory Method)

此模式定义了用于创建对象的接口,但允许子类决定实例化哪个类。 它允许类将实例化延迟到子类。

一个实例化时需要做点初始化工作, 这些工作可能与此类无关,而与业务有关.所以实现代码不要在类中, 而且同时有多个类似的类.
这个时候就用一个工厂类, 将创建实例与使用实例分开. (具体事情做得越多,越容易犯错误)

由于实例化延迟到了子类, 所以改动会修改客户端的实例化代码. 这个时候利用反射 解决分支判断.

抽象工厂(Abstract Factory)

提供一个创建一系列相关或相互依赖对象的接口, 而无需指定它们具体的类

建造者模式(Builder)

此模式将复杂对象的构造与其表示分开,以便相同的构造过程可以创建不同的表示。

主要用于创建一些复杂的对象, 这些对象内部构建间的顺序通常是稳定的, 但对象内部的构建通常面临复杂的变化.
比如收银台的结算过程: 统计订单结算金额 -> 统计订单积分 -> 增加商品销售记录 -> 扣除会员余额,增加会员积分. 这个过程是稳定的, 但是统计积分的实现等都可能变化

其中最重要的就是需要一个指挥者类(Director)用来控制建造过程

原型模式(Prototype)

此模式指定使用原型实例创建的对象类型,并通过复制此原型来创建新对象。

单例模式 (Singleton)

保证一个类有一个实例,病提供一个访问它的全局访问点(比如静态的GetInstance方法)。


结构型 (Gof Structural Patterns)

适配器模式 (Adapter)

将一个类的接口转换成客户希望的另一个接口, Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.

适配器模式有2种类型, 类适配器对象适配器, 由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配, 基本只有c++支持,所以主要讲解对象适配器

桥接模式 (Bridge)

将抽象部分与它的实现部分分离,使它们都可以独立的变化

实现系统可能有多角度分类,每一种分类都可能变化,那么就把这种多角度分离让它们独立变化,减少它们之间的耦合。当发现需要多角度去分类实现对象,而只用继承会造成大量的类增加,不能满足开放-封闭原则时就应该考虑桥接模式。

比如:

‘’手机‘’既可以按照品牌来分类,也可以按照功能来分类

按照品牌分类:手机品牌-手机品牌A-手机品牌A通讯录

按照功能分类:手机软件-通讯录-手机品牌A通讯录

由于实现的方式有很多种,桥接模式的核心意图就是把这些实现独立出来,让它们各自变化。

手机品牌A、手机品台B 合成(Composition)了手机品牌

通讯录、游戏合成(Composition)手机软件

手机品牌包含手机软件,但软件并不是品牌的一部分, 所以它们之间是聚合关系.

组合模式 (Composite)

将对象组合成树型结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的试用具有一致性。(例如Word文档能对单个字进或整个文本进行一样的处理, 又比如TreeView控件就是典型的组合模式应用)

适用场景:发现需求中是体现部分与整体层次的结构时, 以及希望用户可以忽略组合对象与单个对象的不同, 统一使用组合结构中的所有对象时,就应该考虑组合模式。

装饰模式(Decorator)

此模式将附加职责附加到对象,动态地为子类化提供灵活的替代,以扩展功能。

动态的给一个对象添加一些而外的职责, 就增加功能来说, 装饰模式比生成子类更灵活.

外观模式(Facade)

此模式为子系统中的一组接口提供统一接口。 它定义了一个更高级别的接口,使子系统更易于使用。

在经典的三层架构中. 数据访问层 和 业务逻辑层 , 业务逻辑层 和 表示层 在层与层之间建立外观Facade, 这样为复杂的子系统提供一个简单的接口, 减低耦合度.

在维护大型遗留项目时, 这个系统可能非常难以维护和扩展,. 但是由于它包含重要的功能, 新的需求依赖与它. 此时就可以设计一个外观Facade类, 来提供设计粗糙或高度复杂的遗留代码比较清晰的接口

享元模式 (Flyweight)

运用共享技术有效地支持大量细粒度的对象。

代理模式(Proxy)

此模式为另一个对象提供代理或占位符以控制对它的访问。

  • 代理模式的应用
    • 远程代理. 为一个对象再不同的地址空间提供局部代表. 这样可以隐藏一个对象存在于不同的地址空间的事实.
    • 虚拟代理. 根据需要创建开销很大的对象. 通过它来存放实例化需要很长时间的真实对象. 比如HTML网页里很多文字和图片. 就是通过虚拟代理来代替真实的图片, 此时代理存储了真实图片的路径和尺寸.
    • 安全代理. 用来控制真实对象访问时的权限.
    • 智能指引. 指当调用真实的对象时, 代理处理另一些事情. 比如计算对象引用次数, 没有引用时自动释放,

行为型 (Gof Behavioral Patterns)

解释器模式 (Interpreter)

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

解释器需要解决的是:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

比如:常常会在字符串中搜索匹配的字符或判断一个字符串是否有符合规定格式,就会用到正则表达式,正则定义了待匹配字符串的集合,是解释器模式的一种应用,解释器为正则表达式定义了一个文法,如何表示一个特定的正则表达式,以及如何解释这个正则表达式。

模板方法模式(Template Method)

此模式定义操作中算法的框架,将一些步骤推迟到子类。 它允许子类重新定义算法的某些步骤而不改变算法的结构。

当我们要完成某一细节层次一致的过程或步骤, 但是, 个别步骤在更详细的层次上实现可能不同的时候, 通常考虑模板方法模式来处理

职责链模式 (Chain of Responsibility)

使多个对象都有机会处理请求,从而避免请求的发送者和接受者的耦合关系。讲这个对象连成一条链,病沿着这条链传递该请求,直到有一个对象处理它为止。

命令模式 (Command)

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销操作。

迭代器模式 (Iterator)

提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。

中介者模式 (Mediator)

用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。

备忘录模式 (Memento)

在不破坏封闭性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以讲对象恢复到原先保存的状态。

试用场景:用于功能比较复杂,但需要维护或记录属性历史的类。

观察者模式(Observer)

此模式定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖项。

状态模式 (State)

当一个对象的内在状态改变时, 允许改变其行为, 这个对象看起来像是改变了其类.

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况. 吧状态的判断逻辑转移到表示不同状态的一系列类当中, 可以把复杂的判断逻辑简化.

策略模式(Strategy)

此模式定义了一系列算法,将每个算法封装起来并使它们可互换。 它允许算法独立于使用它的客户端。

定义一系列算法的方法, 所有这些算法完成相同的工作, 但是实现不同, 它们可以以相同的方式调用所有算法, 减少各种算法类与使用算法类之间耦合

访问者模式 (Visitor)

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提定义下作用于这些元素的新操作。


六大原则

开放-封闭原则(Open Close Principle)(OCP)

软件实体(类, 模块, 函数等)应该可以扩展, 但是不可修改.

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。

里氏代换原则(Liskov Substitution Principle)(LSP)

软件实体如果使用的是一个父类, 那么一定适用于其子类, 而且它察觉不出父类对象和子类对象的区别. 也就是说, 再软件里面, 把父类都替换成它的子类, 程序的行为没有变化. (子类型必须能够替换掉他们的父类型)

LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用. 里氏代换原则是对“开放-封闭”原则的补充. 实现“开放-封闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范

依赖倒转原则(Dependence Inversion Principle)(DIP)

A. 高层模块不应该依赖底层模块. 两个都应该依赖抽象.
B. 抽象不应该依赖细节. 细节应该依赖与抽象

比如: 开发时候为了复用, 把常用代码写成许多函数库, 项目要访问数据库时, 访问数据库的代码就写成函数, 这样高层就依赖了底层模块.
但是如果做新项目时, 发现业务逻辑的高层模块都是一样的, 但是客户希望使用不同的数据库或存储方式. 这时候就没法复用高层模块了, 应该它们依赖与底层模块. 就像CPU, 内存都依赖于主板, 主板坏不能所有部件都没用, 反过来内存坏了也不应该导致其他部件不能用. 所以都应该依赖抽象, 只要接口是稳定的, 那么就不用担心.

接口隔离原则(Interface Segregation Principle)(ISP)

使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合

迪米特法则(Demeter Principle)(DP)

最少知道原则.一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立

如果两个类不必彼此直接通信, 那么这两个类就不应当发生直接的互相作用. 如果其中一个类需要调用另一个类的某一方法, 可以通过第三者转发这个调用

合成复用原则(Composite Reuse Principle)(CRP)

尽量使用合成/聚合的方式,而不是使用继承。 因为继承是一种强耦合的结构,不是任何时候都可以使用。

合成/聚合服用原则的好处: 有限使用对象的合成/聚合有助于保持每个类被封装,并被集中在单个任务上,这样类和类继承层次就会保持较小规模,并且不太可能增长为不可控的庞然大物。

合成(Composition)聚合(Aggregation)都是关联的特殊种类。

合成(Composition)

合成表示一种拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

比如: 大雁有两只翅膀,翅膀域大雁是部分和整体的关系,并且生命周期相同,于是大雁和翅膀就是合成关系

聚合(Aggregation)

聚合表示一种拥有关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;

比如:大雁是群居动物,所以每只大雁都属于一个雁群,一个雁群可以有多只大雁,所以大雁和雁群是聚合关系


UML类图

  • Class 矩形框表示, 分3层.
    • 第一层显示类名称. (抽象类用斜体),
    • 第二层是类的特性, 通常是字段和属性.
    • 第三层就是类的操作.通常是方法或行为
  • Interface 接口图有2种表示方法
    • 名称前面有<<interface>>
    • 棒棒糖表示法
  • 继承关系用空心三角形+实线表示
  • 关联(association)使用实线箭头表示
  • 依赖关系(dependency)用虚线箭头表示
    • 聚合(aggregation)关系表示一种弱的拥有关系, 比如一群燕 包含 一只燕, A可以包含B,但B不是A的一部分. 使用空心菱形+实线箭头表示
  • 合成(composition)表示强的拥有关系, 提现了严格的部分和整体的部分, 且生命周期一样, 使用实心菱形+实线箭头表示. 比如鸟和翅膀, 连线的2端有数字1和数字2, 表示一只鸟可以有2只翅膀, 无限多用n表示, 聚合关系野可以有基数
  • 符号
    • + 表示 public
    • - 表示 private
    • # 表示 protected

脚注

GOF23种设计模式精解
利用反射和策略模式解决大量switch case

posted @ 2018-09-21 09:40  学友2000  阅读(283)  评论(0编辑  收藏  举报