设计模式

设计模式

  • 聪明的程序员用50%~70%的时间来思考, 尝试和权衡各种设计的实现, 而用30%~50%的时间来编码, 调试和测试.
  • 设计模式使人们可以更加简单方便地复用成功的设计和体系结构.
  • 设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信对象的描述
  • 23个设计模式:
    • 抽象工厂(abstract factory): 提供一个创建一系列相关或相互依赖对象的接口, 而无须指定它们的具体类.
    • 适配器(adapter): 将一个类的接口转换成客户希望的另一个接口, adapter模式使的原本接口不兼容的类可以一起工作.
    • 桥梁(bridge): 将抽象部分与它的实现部分分离, 使他们都可以独立的变化.
    • 构建(builder): 将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以穿件不同的表示.
    • 责任链(chain of responsibility): 为了消除请求的发送者和接收者之前的耦合, 而使多个对象都有机会处理这个请求.
      • 将对象连成一条链, 并沿着这条链传递该请求, 直到有一个对象处理它.
    • 请求(command): 将一个请求封装为一个对象, 从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志, 以及支持可取消的操作.
    • 综合(composite): 将对象组合成树型结构以表示"部分-整体"的层次结构.
      • composite使得客户对单个对象和复合对象的使用具有一致性.
    • 装饰(decorator): 动态地将一个对象添加一些额外的职责, 就功能而言, decorator模式比生成子类方式更加灵活.
    • 表面(facade): 为子系统中的一组接口提供一个一致的界面, facade模式定义了一个高层的接口, 这一接口使得子系统更加容易使用.
    • 工厂方法(factory method): 定义一个可用于创建对象的接口, 让子类决定将哪一个类实例化.
      • factory method模式使一个类的实例化延迟到其子类.
    • 轻量级(flyweight): 运用共享技术有效地支持大量细粒度的对象.
    • 翻译员(interpret): 给定一个语言, 定义它的文法的一种表示, 并定义一个解释器, 该解释器使用该表示来解释语言中的句子.
    • 迭代器(iterator): 提供一种方法顺序访问一个聚合对象中各个元素, 而又不需要暴露该对象的内部表示.
    • 中介者(mediator): 用一个中介对象来封装一系列的对象交互.
      • 中介者使各个对象不需要显式地相互引用, 从而使其耦合松散, 而且可以独立地改变它们之间的交互.
    • 记录薄(memento): 在不破坏封装性的前提下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态.
      • 这样可以将该对象恢复到保存的状态.
    • 观察者(observer): 定义对象间的一种一对多的依赖关系, 以便当一个对象的状态发生改变时, 所有依赖于它的对象都得通知并自动刷新.
    • 原型(prototype): 用原型实例指定创建对象的种类, 并且通过拷贝这个类型来创建新的对象.
    • 代理(proxy): 为其他对象提供一个代理以控制对这个对象的访问.
    • 单例(singleton): 保证一个类仅有一个实例, 并提供一个访问它的全局访问点.
    • 状态(state): 允许一个对象在其内部状态改变时, 改变其行为. 对象看起来修改了其所属的类.
    • 策略(strategy): 定义一系列算法, 把他们一个个封装起来, 并且他们可相互替换.
      • 策略模式使得算法的变化可独立与使用它的客户.
    • 模板方法(template method): 定义一个操作算法的骨架, 而将一些步骤延迟到子类中.
      • 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.
    • 参观者(visitor): 表示一个作用于某个对象结构中的各个元素的操作.
      • visitor模式使你可以在不改变各个元素的类的前提下, 定义作用与这些元素的新操作.
  • C++实现23个设计模式.
  • Java实现23个设计模式.
    设计模式类型
  • 类模式只指那些集中处理类间关系的模式, 而大部分模式都术语对象模式的范畴.
    设计模式之间的关系
    模式可变方面
类型 描述
创建型模式(creactional patterns) 用于构建对象, 以便他们可以从实现体系中分离出来
结构型模式(structural patterns) 用于在许多不同的对象之间形成大型对象结构
行为型模式(behavioral patterns) 用于管理对象之间的算法, 关系和职责
  • 设计模式遵循的几个原则:
    • 单一功能原则: 每个类都应该有一个单一的功能.
    • 里式替换原则: 派生类(子类)对象能够替换其基类(父类)对象被使用. 父类的对象如果替换为子类的对象,他原来执行的行为依然保持不变.
    • 依赖倒置原则: 解耦软件模块的特殊形式, 传统的依赖关系建立在高层次, 二聚体的策略设置应用在低层次上.
      • 使用依赖倒置原则, 使得高层独立与底层的实现细节, 依赖关系被倒置, 使得底层次模块依赖于高层次模块的抽象.
      • 高层模块不应该依赖于低层模块,双方都要依赖于抽象类.
      • 抽象类不应该依赖于实现细节,实现细节应该依赖于抽象.
    • 接口隔离原则: 系统解耦和, 从而容易重构, 更改和重新部署, 将庞大的接口更改为更小和更具体的接口.
    • 迪米特法则: 一种软件开发的设计指导原则.
      • 每个单元对于其他的单元只能拥有有限的知识: 只是与当前单元紧密联系的单元.
      • 每个单元只能与它的朋友交换数据, 不能和陌生单元交换数据.
      • 之和直接的朋友交谈.
      • 对象与对象之间尽量相互独立,具体对象的行为由具体的对象去完成,而不是由某个对象去指定另一个对象去实施行为而且是具体的行为.
      • 弱化耦合关系可以提升复用率,但是这样的话,会产生中间的跳转类等,导致系统复杂.
      • 高内聚, 低耦合.
    • 开闭原则: 软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的.
      • 一个实体允许在不改变它源码的前提下改变其行为.
  • 几个原则的归纳总结:
    1. 单一职责原则是从类的功能的角度去设计,将不同的职责分别归于不同的类中,这样使得设计更加清晰、易修改。
    2. 里氏替换原则是从类的继承的角度去设计,强调父类被子类继承后,在父类出现的地方,可以替换为其子类,而且行为不会发生变化。
    3. 依赖倒置原则是从类之间依赖关系的角度去设计,强调高层不依赖于低层,低层依赖于高层,减少实现细节的依赖。
    4. 接口隔离原则是从接口的方法设计角度去思考,强调不能过度设计接口,尽量使得接口内的方法能充分的提供给其实现类需要的功能,不要过度设计导致方法的冗余,也不要设计不充分导致,实现类中有未能抽取的公共部分。
    5. 迪米特法则或最少知识原则,从类与类之间耦合的角度去思考,降低耦合,减少不必要的耦合,不要跨越多层去调用方法,最佳的方式是只调用其朋友类的方法,之后行为由朋友类负责实施。
  • 对设计模式不错的解读.

创建型设计模式

单例模式(Single patterns)

  • C++单例模式.
    • 单一的类, 该类负责创建自己的对象, 同时确保只有一个对象被创建.
    • 提供一种访问的唯一方式, 可以直接访问, 不需要实例化该类的对象.
    • 具体实现:
      • 单例类有且仅有一个实例. --- 提供一个private构造函数(防止外部调用而构造类的实例).
      • 单例类必须自行创建自己的唯一实例. --- 提供一个该类的static private对象.
      • 单例类必须给所有其它对象提供这一实例. --- 提供一个static public函数, 用于创建或获取其本身的静态私有对象.
  • 线程安全(双检锁 - DCL, 即double checked locking).
  • 资源释放.
  • 懒汉式:
    • lazy初始化的特性, 尽量把初始化延迟. --- 第一次调用才初始化, 避免内存浪费.
    • 非多线程安全. --- 必须加锁.
  • 饿汉式:
    • 非lazy初始化, 线程安全, 但类加载时就初始化, 浪费内存.

简单工厂模式, 工厂方法模式, 抽象工厂模式

  • 将类的创建权利授权于工厂类, 其他的类不允许穿件, 需要某些类就去工厂中获取.
    • 不关心对象怎么来的, 只关心对象怎么用.

简单工厂

  • 简单工厂又叫静态工厂方法, 由一个工厂类根据传入的参数, 动态决定应该创建哪一个产品的实例.
  • 直接实现一个方法,要生产什么由这个方法以及传入的参数来决定.
  • 工厂类集中了所有产品的创建逻辑(违反了高内聚责任分配原则);
  • 一旦添加新产品就不得不修改工厂逻辑.
  • 简单工厂通过参数控制生产不同的产品.

工厂方法

  • 封装类中的不变部分, 提取其中个性化善变的部分为独立类, 通过依赖注入以达到解耦复用的目的.
  • 抽象工厂: 是工厂方法模式的核心, 与应用程序无关, 任何在模式中创建的对象的工厂类必须实现这个接口.
  • 具体工厂: 实现抽象工厂接口的具体工厂类, 包含与应用程序紧密相关的逻辑, 并且被应用程序调用以创建产品对象.
  • 抽象产品: 所创建对象的基类, 具体产品的共同父类, 或共同拥有的接口.
  • 具体产品: 实现了抽象产品角色所定义的接口, 某具体产品有专门的具体工厂创建爱你, 他们之间往往一一对应.
  • 每增加一个产品, 必须增加一个子工厂, 加大了额外的开发量.
  • 工厂方法模式有点类似于专车的样子,我只能生产这个东西,你只能生产那个东西,比较专一.

抽象工程

  • 抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式.
  • 抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象.
  • 抽象工厂可以产生不同类型的产品.
  • 抽象工厂解决了生产一种产品不同类型的产品的问题.
  • 抽象的工厂类定义了其子类可以生产不同的产品,不同的工厂可以实现生产 不同型号的同一种类的产品.

创建者模式

  • 创建者(生成器模式)是一种对象构建模式. 建造者模式可以设置不同的属性或者行为,创建出不一样的对象.
  • 建造者模式(Builder Pattern),旨在将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.
  • 抽象创建者: 为创建一个产品对象的各个部件指定抽象接口.
  • 具体建造者: 实现builder的接口以构造和装配该产品的各个部件, 定义并明确它所创建的表示, 提供检索产品的一个接口.
  • 指挥者(director): 构造一个使用builder接口的对象.
  • 产品: 表示被构造的复杂对象.
  • 与工厂方法模式比较,创建者关注的是零件的类型和装配的顺序,但是工厂方法关注的是创建一个个的对象,可以是零件,但是它不关注组装顺序.

原型模式

  • 通过复制一个实例来返回新的实例, 而不是新建实例。
  • 原型模式多用于创建复杂的或者耗时的实例, 通过复杂一个已存在的实例提升效率。
  • 原型模式创建对象不会调用构造函数进行创建。直接从内存去赋值对象的数据,忽略构造函数的限制,他和之前提到的单例模式有所冲突,单例模式会将构造函数设置为私有,而原型模式却可以绕过构造函数去通过内存中的对象的数据区创建对象,所以这个地方就会有所冲突。
  • 深拷贝和浅拷贝的区别.
    • 对数组, 容器对象, 对象等可能需要进行深拷贝。
    • 浅拷贝只拷贝基本数据类型和string类型(内置类型)。
  • 抽象原型, 定义了克隆自身的接口。
  • 具体原型, 被赋值的对象, 需要实现prototype定义的接口。

结构型设计模式

适配器模式

  • 适配器模式是一种补救模式, 将一个类的接口转换为客户希望的另一个接口, 从而兼容地工作。
    • 对象适配器。--- 对象组合的方式。
    • 类适配器。--- 多继承的方式。
  • 目标接口: 所期望得到的接口。
  • 适配者: 需要适配的类。
  • 适配器: 将适配者包装为目标接口。
  • 过多地使用适配器,会让系统非常零乱,不利于整体把控.
  • 适配器模式可以让两个不是很一致的类在一起运行.

代理模式

  • 代理模式(Proxy Pattern)为其他对象提供了一种代理,以控制对这个对象的访问.
  • 在某些情况下, 一个对象不适合或者不能直接引用另一个对象, 而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 使用场景:
    • 远程代理;
    • 虚拟代理;
    • 保护代理;
    • 缓冲代理;
    • 智能引用代理。
  • 代理模式分为两种,一种是静态代理模式,另外一种是动态代理模式.
    • 静态代理模式是代理类为其创建一个对象,将需要代理的类的对象赋予代理类中的该对象,让代理类中该对象去代替需要代理的类的对象去执行一定的任务。
    • 动态代理模式采用JDK的动态代理,根据其类加载器以及其实现的接口生成代理对象,通过反射机制实现动态代理。

装饰模式

  • 装饰模式是在不必改变原类人间和使用继承情况下, 动态地扩展一个对象的功能。
    • 通过创建一个包装对象。
  • 装饰模式的优缺点.

外观模式

  • 外观模式为子系统中的一组接口提供一个统一的高层接口, 该接口使得子系统更加容易使用。
  • 当一个项目中出现比较杂乱的调用关系时,类与类之间的耦合关系过于复杂,为了降低耦合度.
    • 有多个不同的类提供了多个访问的接口,外观模式就是要统一将这些接口进行管理.

桥梁模式

  • 桥接模式是软件设计模式中最复杂的模式之一,它把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化.
    • 桥接模式将抽象部分和实现部分分离, 使得他们都可以独立地变化。
  • 桥梁模式的核心在于解耦,通过抽象化将具体的事物抽象成一般的事物,也就是具有共性的东西,将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联.
    • 在一个软件系统的抽象化和实现化之间使用聚合关系而不是继承关系,从而使两者可以相对独立地变化.

组合模式

  • 组合模式又称部分-整体模式,主要是用来描述部分与整体的关系, 计生对象组合用整体与部分层次表示, 使用户对单个对象和组合对象的使用具有一致性。
    • 通过接口对子对象统一操作。
  • 组合模式有两种类型: 透明组合模式和安全组合模式。
    • 透明组合:
      • 在 Component 中定义了用于访问和管理子构建的接口,这样做的好处是确保所有的构件类都有相同的接口.
      • 在 Client 看来,Leaf 与 Composite 所提供的接口一致,Client 可以相同地对待所有的对象.
    • 安全组合:
      • 在 Component 中不定义任何用于访问和管理子构建的接口,而在 Composite 中声明并实现.
      • 因为不需要向 Leaf 提供这些管理成员对象的接口,对于 Leaf 来说,Client 不可能调用到这些接口, 所以叫安全组合。

享元模式

  • 享元模式(Flyweight Pattern)运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用.
    • 高复用。
  • 使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件.
  • 通常物件中的部分状态是可以分享.
  • 常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元.

行为型设计模式

模板方法模式

  • 模板方法模式定义一个操作中算法的骨架,而将一些步骤延迟到子类中。
    • 模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
  • 抽象定义好模板类, 子类通过继承并实现其父类中定义好的模板中需要执行的具体的方法, 调用子类对象时,会执行该类中的具体实现的方法。

观察者模式

  • 观察者模式定义了对象之间的一对多的依赖关系, 让多个观察者同时监听某一个主题对象(被观察者)。
  • 可以检查一些实时更新。
  • 在发生某些事件的时候,通过notify“通知”观察者,完成相应的操作.

策略模式

  • 策略模式使算法独立于使用算法的客户端。定义一系列算法, 把他们封装起来, 并且使他们可以互相替换。
  • 策略模式从名字就可以看出,有多种选择,不同的策略对应着不同的实现方式,那么一般的形式为一个接口采用多种实现方式(也就是提供了不同的策略),然后再提供一个策略的选择类即可.
  • 策略模式可以避免多重判断。

状态模式

  • 状态模式应该说可以理解为某种状态下,程序的执行流程可能会发生变化,类似于交通灯,红灯的时候停下,绿灯行走,黄灯时等一等,这就是三种状态下我们人对其作出的相应的变化。
  • 再比如,公交车应该都坐过的,公交车停车的时候,可以上车和下车;公交车行驶的时候,不允许下车和上车,那么,这里公交车停车和行驶是两种状态,这两种状态对于后续人上车下车的行为产生了一定的影响。
  • 通俗点就是说,某种状态作为先决条件时,后面的行为受到了前面的这种状态的影响,这种情况比较适合运用状态模式.

职责链模式

  • 职责链模式使多个对象都有机会处理请求, 从而避免请求的发送者和接收者之间的耦合关系。
    • 将对象连成一条链, 并沿着这条链传递该请求,直到有一个随想处理它为止。
  • A->B->C这样的一条链表,在责任链模式中,请求交给A进行处理,如果A处理不了交给B,B如果处理的了进行处理,否则交给C处理,模式的关键在于构建这样的一个链表,并且完成链表之间这些处理情况的切换,这一点在定义中应该是说可以把不能处理的命令对象传递给链中的下一个处理对象.

命令模式

  • 命令模式是一种数据驱动的设计模式, 请求以命令的形式封装在对象中, 并传递给调用对象。
    • 调用对象寻找可以处理该命令的合适对象, 并把该命令传递给相应的对象, 并执行命令。
  • 命令模式是一个高内聚的模式,它将一个请求封装为一个对象,让你使用不同的请求将客户端参数化,在项目中应用比较广泛,他的封装性比较好,将请求方和接收方分离开来,扩展性较好.
  • 接收者: 负责具体执行那些动作, 只提供一些任务的具体实现, 并不负责其他的部分。
  • 命令角色: 负责定义好具体的命令,包括规定好接收者需要执行的那些动作。
  • 调用者角色: 负责调用命令执行, 不需要知道谁是接收者。

访问者模式

  • 访问者模式表示一个作用与某对象的各个元素的操作, 它使可以在不改变各个元素类的前提下定义作用于这些元素的新操作。
  • 业务规则要求遍历多个不同的对象。访问者模式是对于迭代器模式的扩充,他可以访问不同的对象,实现遍历的目的.
  • 元素角色只负责数据的加载,Visitor中负责报表的展示.

中介者模式

  • 用一个中介对象来封装一系列的对象交互, 中介者使各个对象不需要显示地互相引用, 使其耦合松散, 可以独立地改变他们之间的交互。
  • 当多个对象之间存在着过多的耦合时,可以通过中介者模式进行解耦.
  • 将具体的对象之间的耦合转为中介者与具体对象的耦合.

忘备录模式

  • 捕获一个对象的内部状态, 并在该对象之外保存这个状态, 这样可以将对象恢复到原先保存的状态。
  • 备忘录模式是用于将对象的状态暂存在某些特殊情况下可以将其进行恢复的模式,可以通过多种方式实现,包括clone以及一般方式以及多种参数的备忘录等形式.
  • 标准的备忘录在项目中很难直接应用进去,多数为其变形后的处理方式.

解释器模式

  • 解释器模式在项目中很少使用,因为他会引起效率、性能以及维护等问题。
  • 解释器模式一般用来解析比较标准的字符集,比如说SQL语法分析等.
  • 执行效率比较低,可利用场景比较少.
posted @ 2019-05-01 17:15 coding-for-self 阅读(...) 评论(...) 编辑 收藏