23种设计模式 纯理论 笔记

设计原则

产业强盛的原因: 接口标准化

1.依赖倒置原则(DIP)

  • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定).
  • 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定).

2.开放封闭原则(OCP)

  • 对扩展开放,对更改封闭
  • 类模块应该是可扩展的,但是不可修改.

3.单一职责原则(SRP)

  • 一个类应该仅有一个引起它变化的原因.
  • 变化的方向隐含着类的责任.

4.Liskov 里氏替换原则(LSP)

  • 子类必须能够替换他们的基类(is-a)
  • 继承表达类型抽象

5.接口隔离原则(ISP)

  • 不应该强迫客户程序依赖它们不用的方法.
  • 接口应该小而完备

6,优先使用对象组合,而不是类继承

  • 类继承通常为"白箱复用",对象组合通常为"黑箱复用".
  • 继承在某种程度上破坏了封装性,子类父类耦合度高.
  • 而对象组合则只要求被组合的对象具有良好的定义的接口,耦合度低.

7.封装变化点

  • 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合.

8.针对接口编程,而不是针对实现编程

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口
  • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口.
  • 减少系统中各部分的依赖关系,从而实现"高内聚,低耦合"的类型设计方案.

将设计原则提升为设计经验

  1. 设计习语
  2. 设计模式 (变化中的复用关系)
  3. 架构模式

Gof-23种模式分类

组件协作模式: Template Method . Strategy Observer/Event

​ 现代软件专业分工之后的第一个结果是"框架与应用程序的划分","组件协作"模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作常用的模式.

1.模板方法模式

​ 定义一个操作种的算法的骨架,而将一些步骤延迟到子类中. Template Method使得子类可以不改变一个算法的结构即可重定义(Override)该算法的特定步骤.

  • Template Method模式是一种非常基础的设计模式,在面对对象系统中有着大量的应用.它用最简洁的机制为很多应用程序框架提供了灵活的扩展点,是代码复用最基本实现结构
  • 除了可以灵活应对子步骤变化外,"不要调用我,让我调用你"的反向控制结构是Template Method的典型应用 (类似于servlet重写doget/post方法)
  • 在具体实现方面,被Template Method调用的虚方法可以被实现,也可以没有任何实现(抽象方法,纯虚方法),一般推荐把它们设置为protected方法. (往往是要放到一个流程里面才有意义,而不是用Public方法供外界调用.)

2.Strategy 策略模式

​ 定义一系列算法,把它们一个个封装起来,并且使他们可互相替换,该模式使得算法可独立于使用它的客户程序而变化(扩展,子类化)

  • Strategy及子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换…
  • Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合.含有许多条件判断语句的代码通常都需要Strategy模式
  • 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销.

模板模式和策略模式的区别:执行流程,可变节点,重载侧重点

3.Observer 观察者模式

​ 定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所以依赖它的对象都得到通知并自动更新.

  • 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合
  • 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播.
  • 观察者自己决定是否需要订阅通知,目标对象对此一无所知.
  • Observer模式使基于事件的UI框架中非常常见的设计模式,也是MVC模式的一个重要组成部分.

============================================================================================

单一职责模式: (Decorator装饰模式 Bridge桥模式)

  • 在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往都是随着需求的变化,子类如果急剧膨胀,同时充斥着相符代码,这时候的关键是划清责任

4.Decorator 装饰模式

​ 动态(组合)地给一个对象增加一些额外的职责.就增加功能而言,Decorator模式比生成子类(继承)更加灵活(消除重复代码,减少子类个数,代码功能美化)

  • 通过采用组合而非继承的手法,Decoratro模式实现了在运行时动态扩展对象的能力,而且可以根据需要扩展多个功能.避免了使用继承带来的"灵活性差"和"多子类衍生问题".
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口.但在实际表现上表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类.
  • Decorator模式的目的并非解决"多子类衍生的多继承"问题,Decorator模式应用的要点在于解决**“主体类在多个方向上的扩展功能”**—是为"装饰"的含义

5.Bridge 桥模式

​ 将抽象部分(业务功能)与实现部分(平台实现)分离,使得它们可以独立的变化

  • Bridge模式使用"对象间的组合关系"解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化. 所有抽象和实现沿着各自维度的变化,即"子类化"它们.
  • Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性较差,Bridge模式是比多继承方案更好的解决方法.
  • Bridge模式的应用一般是在"两个非常强的变化维度",有时候一个类在多于两个的变化维度,这时可以使用Bridge的扩展模式

=========================================================================================

对象创建模式 (Factory Method, Abstract Factory, Prototype, Builder)

​ 通过"对象创建"模式绕开new,来避免对象创建过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定,它是接口抽象之后的第一步工作.

6.Factory Method 工厂方法模式

​ 定义一个用于创建对象的接口,让子类决定实例化哪个类. Factory Method使得一个类的实例化延迟(目的:解耦)到子类

主要解决:主要解决接口选择的问题。

何时使用:我们明确地计划不同条件下创建不同实例时。

如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

  • Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系.面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱
  • Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现了一种扩展的策略,较好地解决了这种紧耦合关系.
  • Factory Method模式解决了"单个对象"的需求变化,缺点在于要求创建方法/参数相同

7.Abstract Factory Method 抽象工厂模式

​ 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。(一个超级工厂创建其他工厂)

主要解决:主要解决接口选择的问题。

何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

如何解决:在一个产品族里面,定义多个产品。

关键代码:在一个工厂里聚合多个同类产品。

优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

8.Prototype Pattern 原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。(通过拷贝一个现有对象生成新对象)

浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流

​ 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

主要解决:在运行期建立和删除原型。

何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。

优点: 1、性能提高。 2、逃避构造函数的约束。
缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。

使用场景:1.资源优化 2.类初始化非常消耗资源 3.性能和安全要求 4.new对象非常繁琐 5.一个对象多个修改这 6.一个对象需要提供给其他对象访问,而且各个调用者都可能需要修改值. 7.一般和工厂方法模式一起出现,通过clone方法创建对象,然后由工厂方法提供给调用者.

9.Builder Pattern 建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。

​ 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

JAVA 中的 StringBuilder, 与工厂模式的区别是:建造者模式更加"关注与零件装配的顺序"。

主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

何时使用:一些基本部件不会变,而其组合经常变化的时候。

如何解决:将变与不变分离开。

优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。

==================================================================================================================================================================================================================================================================================================================================================================================================================================================

对象性能模式 (Singleton Flyweight)

  • 面向对象很好地解决了"抽象"的问题,但是必不可免要付出一定的代价.对于通常情况来讲,面向对象的成本大都可以忽略不计.但是某些情况,面向对象所带来的成本必须谨慎处理.

10.Singleton 单例模式

​ 保证一个类仅有一个实例,并提供一个该实例的全局访问点.

  • 单例模式重的实例构造器可以设置为protected以允许子类派生
  • 单例模式一般不支持拷贝构造函数和Clone,因为这有可能导致多个对象实例,与单例模式的初衷违背.
    • 如何实现多线程环境下安全的单例模式?注意对双检查锁的正确实现

11.Flyweight 享元模式

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

  • 面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题,Flyweight主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题.
  • Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力.在具体实现方面要注意对象状态的处理.
  • 对象的数量太大从而导致对象内存开销加大----什么样的数量才能算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断.

==================================================================================================================================================================================================================================================================================================================================================================================================================================================

接口隔离模式(Facade Proxy Adapter Mediator)

​ 在组件构成过程中,某些接口之间的直接依赖常常会带来很多问题,甚至根本无法实现.采用添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口是一种非常常见的解决方法.

12.Facade 门面模式

​ 用稳定的接口隔离变化体.

  • 从客户程序的角度来看,Facade模式简化了整个组件系统的接口,对于组件内部与外部客户程序来说,达到了一种"解耦"的效果—>内部子系统的任何变化不会影响到Facade接口的变化.
  • Facade设计模式更注重从架构的层次去看整个系统,而不是单个类的层次.Fcade很多时候更是一种架构设计模式.
  • Facade设计模式并非一个集装箱,可以任意的放进任何多个对象.Fcade模式中组件的内部应该是"互相耦合关系比较大的一系列组件"么人不是一个简单的功能集合.

13.Proxy 代理模式

​ 动机:在面向对象的系统中,有些对象由于某种原因(比如创建用户的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者,或者系统结构带来很多麻烦.如何在不失去透明操作对象的同事来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方式.

​ 为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问.

  • "增加一层间接层"是软件系统中对许多复杂问题的一种常见解决方法.在面向对象系统中,直接使用某些对象会带来很多问题,.作为间接层的proxy对象便是解决这一问题的常用手段
  • 具体proxy设计模式的实现方法,实现粒度都相差很大,有些可能是对单个对象做细粒度的控制,如copy-on-writer技术,有些可能对组件模块提供抽象代理层,在架构层次对对象做proxy.
  • Proxy并不一定要求保持接口完整的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的.

14.Adapter 适配器模式

动机:由于应用软件变化,常常需要将"现存对象"放在新环境中应用,但是新环境要求的接口是这些现存对象所不满足的.

​ 将一个类的接口转换成客户希望的另一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.(不推荐使用,用对象适配器)

  • Adapter模式主要应用于"希望复用一些现存的类,但是接口又与复用环境条件不一致的情况",在遗留代码复用,类库迁移等方面非常有用.
  • Gof 23 定义了两种Adapter模式的实现结构:对象适配器的类适配器.但类适配器采用"多继承"的实现方式,一般不推荐使用.对象适配器采用"对象组合"的方式,更符合松耦合精神
  • Adapter模式可以实现的非常灵活,不必拘泥于Gof23中定义的两种结构.例如:完全可以将Adapter模式中的"现存对象"作为新的接口方法参数,来达到适配的目的.

15.Mediator 中介者模式

​ 用一个中介对象来封装(封装变化)一系列的对象交互,中介者使各对象不需要显式的相互引用(编译时依赖=>运行时依赖),从而使其耦合松散(管理变化),而且可以独立地改变他们之间的交互./

  • 将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理,变"多个对象互相关联"为"多个对象和一个中介者关联",简化了系统的维护,抵御了变化的可能.
  • 随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂,这时候可以对Mediator对象进行分解处理.
  • Fcade模式使解耦系统间(单向)的对象关联关系.Mediator模式是解耦系统内各个对象之间(双向)的关联关系.

==================================================================================================================================================================================================================================================================================================================================================================================================================================================

状态变化模式 (Stat Memento)

​ 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效管理?同时又维持高层模块的稳定?"状态变化"模式为这一问题提供了一种解决方案.

16.Stat 状态模式

​ 允许一个对象在其内部状态改变的时候改变它的行为.从而使对象看起来似乎修改了其行为.

  • State模式将所有与一个特定状态相关的行为都放入一个Stat的子类对象中 ,在对象状态切换时,切换相应的对象,但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦.
  • 为不同的状态引入不同的对象使得状态转变变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的—>即要么彻底转换过来,要么不转换
  • 如果State对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销.

17.Memento 备忘录(快照)

动机:某些对象状态在转换过程中要求程序能够回溯到对象之前处于某个点时的状态.如果使用一些共有接口来让其他对象得到对象的状态,便会暴露对象的细节实现.

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

  • 备忘录存储原发器对象的内部状态,在需要时恢复原发器状态.
  • Memento模式的核心是信息隐藏,即原发器需要向外界隐藏信息,保持其封装性.但同时有需要将状态保持到外界.,
  • 由于现代语言运行时都具有相当的对象序列化支持,因此往往采用效率较高,又容易正确实现的序列方案来实现Memento模式.

==================================================================================================================================================================================================================================================================================================================================================================================================================================================

数据结构模式(Composite Iterator Chain of Resposibility)

​ 常常有一些组件在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构将极大的破坏组件的复用.这时候将这些特定的数据结构封装在内部,在外部提供统一的接口.来实现与特定数据结构无关的访问,是一种行之有效的解决方案.

18.Composite 组合模式

​ 将对象组合成树形结构以表示"部分-整体"的层次结构. Composite使得用户对单个对象和组合对象的使用具有一致性(稳定).

  • Composite模式采用树形结构来实现普遍存在的对象容器,从而将"一对多"的关系转化为"一对一"的关系.使得客户代码可以一致的(复用)处理对象和对象容器,无需关心处理的是单个的对象.还是组合的对象?
  • 将"客户代码与复杂的对象容器结构"解耦是Composite的核心思想,解耦之后,客户代码将与纯粹的抽象接口—而非对象容器的内部实现结构—发生依赖,从而更能"应对变化".
  • Composite模式在具体实现中,可以让父对象的子对象反向追溯,如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率.

19.iterator 迭代器

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

  • 迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示
  • 迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作.
  • 迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题.

20.Chain of Resposibility 职责链

​ 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止.

  • 责任链模式的应用场合在于"一个请求可能有多个接收者,但是最后真正的接收者只有一个",这时候请求发送者与接收者的耦合可能出现"变化脆弱"的症状,责任链的目的就是将二者解耦,从而更好的应对变化.
  • 应用了责任链模式后,对象的职责分派将更具灵活性.我们可以在运行时动态添加/修改请求的处理职责.
  • 如果请求传递到责任链的末尾仍得不到处理,应该有一个合理的缺省机制.这也是每一个接收对象的责任,而不是发出请求的对象的责任.

==================================================================================================================================================================================================================================================================================================================================================================================================================================================

行为变化模式 (Command Visitor)

​ 在组件的构建过程中,组件行为的变化经常导致组件本身剧烈的变化."行为变化"模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现两者间的松耦合.

21.Command 命令模式

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

  • 命令模式的根本目的在于将"行为请求者"与"行为实现者"解耦.在面向对象语言中,最常见的实现手段是"将行为抽象为对象"
  • 实现命令接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息.通过使用Command 模式可以将"多个命令"封装成一个"符合命令".
  • 命令模式与C++中的函数对象有些类似.但两者定义行为接口的规范有所区别: Command以面向对象中的"接口-实现"来定义行为接口规范,更严格,但有性能损失, C++函数对象以函数签名来定义行为接口规范,更灵活,性能更高.

22.Visitor 访问器

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

​ 主要将数据结构与数据操作分离(在被访问的类里面加一个对外提供接待访问者的接口。)

  • 访问器模式通过所谓双重分发(double dispatch)来实现在不更改(不添加新操作-编译时)Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新操作.
  • 所谓双重分发即访问者模式中间包括了两个多态分发(多态机制):第一个为accept方法的多态辨析.第二个为visitElementX方法的多态辨析.
  • 访问者模式的最大缺点在于扩展类层次结构,会导致Visitor类的改变.因此Vistor模式适用于"Element类层次结构稳定,而其中的操作却经常面临频繁改动."

领域规则模式

​ 在特定领域中,某些变化虽然频繁,但可以抽象为某种规则.这时候,结合特定领域,将问题抽象为语法规则,从而给出在该领域下的一般性解决方案

23.Interpreter Pattern 解释器模式

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

​ 构建语法树,定义终结符与非终结符

  • 解释器模式的应用场合是解释器模式应用中的难点,只有满足"业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题"才适合使用解释器模式
  • 使用解释器模式来表示文法规则,从而可以使用面向对象技巧来方便扩展文法
  • 解释器模式比较适合简单的文法表示,对于复杂的文法表示,解释器模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值