设计模式笔记

  • 程序员设计类的时候,应该先构思出最可能发生变化的种类,然后构造抽象来隔离那些变化(设计模式)
  • 预测很难,但是我们却可以在发生小变化时,就及早去想办法去应对发生更大的变化
  • 在我们最初编写代码的时候,假设不会发生变化。当变化发生时,我们就创建抽象来隔离以后发生的同类变化
  • 开发人员应该仅对程序呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象

1 设计模式定义

  • 设计模式是一套架构设计经验
  • 它帮助我们做出有利于系统的灵活性、复用性、扩展性的选择,抽象出系统的核心、稳定的部分(即是架构),将易变的部分与稳定的部分分离

目的准则

  • 创建型:与对象的创建有关
  • 结构型:处理类或对象的组合
  • 行为型:对类或对象怎样交互和怎样分配职责进行描述

范围准则

  • 类模式:处理类和子类之间的关系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了
  • 对象模式:处理对象之间的关系,这些关系在运行时刻是可以变化的,更具有动态性

组合

  • 创建型类模式:将对象的部分创建工作延迟到子类
    • 工厂模式
  • 创建型对象模式:将对象的部分创建工作延迟到另一个对象中
    • 抽象工厂、建造者、原型模式、单例模式
  • 结构型类模式:使用继承机制来组合类
    • 适配器模式
  • 结构性对象模式:描述对象的组装方式
    • 适配器模式、桥接模式、组合模式、装饰者模式、门面模式、Flyweight、代理模式
  • 行为型类模式:使用继承描述算法和控制流
    • 模板模式
  • 行为型对象模式:描述一组对象怎么协作完成单个对象无法完成的任务
    • 责任链、命令模式、中介模式、发布订阅、策略模式、访问者模式

2 常见设计模式简介

抽象工厂: 提供一个创建一系列相关或相互依赖对象的接口(产品家族),而无需指定它们具体的类

适配器模式: 将一个类的接口转换成客户希望的另外一个接口。它使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

桥接模式: 将抽象部分与它的实现部分分离,使它们都可以独立地变化

建造者模式: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

责任链模式: 为解除请求的发送者和接收者之间的耦合(接收者不固定),而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿者这条链传递该请求,直到有一个对象处理它为止

装饰者模式: 动态地给对象添加一些额外的职责(核心职责是确定的)。就扩展而言,装饰者模式比生成子类方式更为灵活

门面模式: 为子系统中的一组接口提供一个一致的界面,使得子系统更加容易使用和变更

工厂模式: 定义一个用于创建对象的接口,让子类决定将哪一个类实例化。它将一个类的实例化延迟到了其子类中(产品家族中的子产品)

发布订阅模式: 定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都能得到通知并自动刷新

原型模式: 用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象

代理模式: 为对象提供一个代理,以控制对这个对象的访问

单例模式: 保证一个类仅有一个实例,且提供一个访问它的全局访问点

策略模式: 定义一些列算法,把它们封装起来,并使可以相互替换。使得算法的变化可以独立于它的客户

模板模式: 定义一个操作中的骨架,而将一些步骤延迟到子类中

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

命令模式: 将请求封装成一个对象,从而使得你可用不同的请求对客户进行参数化;

组合模式: 将对象组合成树型结构以表示 “部分-整体” 的层次结构。组合模式使得客户对单个对象和复合对象的使用具有一致性

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

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

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

3 每个设计模式中可变的方面

组合设计模式可变的方面不变的反面
创建型类模式工厂模式被实例化的子类创建方法
创建型对象模式抽象工厂产品对象家族创建方法
创建型对象模式建造者模式如何创建一个组合对象对象
创建型对象模式原型模式被实例化的类
创建型对象模式单例模式一个类的唯一实例
结构型类/对象模式适配器模式对象的接口
结构型对象模式桥接模式对象的实现桥接的对象提供的接口
结构型对象模式组合模式一个对象的结构和组成
结构型对象模式装饰者模式对象的职责,不生成子类
结构型对象模式门面模式一个子系统的接口
结构型对象模式代理模式如何访问一个对象,该对象的位置
行为型类模式模板模式算法中的某些步骤
行为型对象模式责任链模式满足一个请求的对象
行为型对象模式命令模式何时、怎样满足一个请求(命令执行者)命令
行为型对象模式发布/订阅模式一个对象被多个未知对象依赖
行为型对象模式策略模式算法执行算法的动作
行为型对象模式访问者模式某些可作用于以一个(组)对象上的操作(访问者),但不修改这些对象的类被访问的元素
行为型对象模式状态模式状态种类转变状态的动作
行为型对象模式备忘录模式一个对象的哪些私有信息存放在对象之外,以及在什么时候进行存储被存储的对象

4 创建型模式

  • 它们都将关于该系统使用哪些具体的类的信息封装起来(仅仅是暴露类相对应的接口

  • 它们隐藏了这些类的实例是如何被创建和放在一起的

  • 因为用户不知道具体的类以及创建和组合过程,所以系统内部具体的类是可以变化的

  • 创建型模式在 什么被创建、谁创建它、它是怎样被创建的,以及何时创建这些方面给予我们很大的灵活性

1 抽象工厂

优点:

  • 分离了具体的类:客户只需要依赖一个抽象工厂,即可通过此抽象工厂得到抽象工厂对应的抽象产品系列

  • 易于改变产品系列:因为客户只依赖抽象工厂,一个抽象工厂创建了一个完整的产品系列,这使得改变抽象工厂对应的具体工厂变得容易

  • 有利于产品的一致性: 每一个具体工厂对应的具体产品系列都是固定不变的

缺点:

  • 难以添加新种类的产品: 如果要在产品系列中增加一个新的产品,需要扩展所有的具体工厂
    在这里插入图片描述

迷宫中的 door wall room,这3个产品就组成了一个系列

DefaultMazeFactory – 一个子类工厂

BombedMazeFactory – 一个子类工厂

抽象工厂,仅仅是创建一个系列的产品,但是产品直接的联系并没有操作

抽象工厂类通常用工厂方法实现

2 builder 模式
  • 和抽象工厂的区别在于它是在客户的控制下一步步组装一个复杂的对象(且隐藏了对象创建与组合的细节),而抽象工厂是创建一个产品系列

迷宫中的 door wall room

建造者模式,可以组合所有产品,形成一个复杂的迷宫类,而隐藏最终的产品组合细节

  • 建造者模式由两部分组成:1、建造者 2、组织者
    • 有不同的建造者子类,重定义每一个建造的部分
    • 组织者:使用 建造者的接口,稳定的、规范的创建对象
3 工厂模式
  • 工厂方法 和 抽象工厂的区别 在于,工厂方法最终只创建一个实例(创建实例的过程中会调用抽象工厂方法获取抽象产品系列,并组合成最终的实例)。抽象工厂仅仅创建产品系列
  • 以上工厂方法说明,过程中用到了模板方法
    在这里插入图片描述
3.1 子类创建对象

在这里插入图片描述

3.2 连接平行的类层次
  • 当一个类将它的一些职责委托给一个独立的类时,就产生了平行类层次
    在这里插入图片描述
3.3 参数化工厂方法(简单工厂)
  • 参数化工厂可以简单的扩展一个子类的行为
public void create(String type) {
    if (type == "1") {
        return new OneProduct();
    }
}
4 Prototype 模式
  • 优点:改变结构以指定新对象。只要复合电路对象将 clone 实现为一个深拷贝,具有不同结构的电路就可以是原型了
  • 缺点:每一个 prototype的子类都必须实现 clone 操作,这是很困难的。当内部包括一些 不支持拷贝或有循环引用的对象时,实现克隆可能也是困难的
  • 从一个对象再创建另外一个可定制的对象
5 Singleton 模式
  • 保证一个类仅有一个实例
6 总结
  • 工厂模式使一个设计可以定制且只略微有一些复杂。其他设计模式需要新的类,而工厂模式只需要一个新的方法
  • 人们通常将 工厂模式 作为一种标准的创建对象的方法。但是当被实例化的类根本不发生变化或当实例化出现在子类可以很容易重定义的操作中时,这就并不必要了
  • 通常,设计以使用 工厂模式开始,并且当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演变

5 结构型模式

  • 结构性模式涉及到如何组合类和对象以获得更大的结构
1 适配器模式
  • 将一个类的接口转换为客户希望的另外一个接口

  • 适配器模式使用多重继承对一个接口与另一个接口进行匹配

  • 它有时还要负责提供那些被匹配的类所没有提供的功能

多重继承

adaptee:以存在的接口,需要适配 target

target:与特定领域相关的接口
在这里插入图片描述

对象组合
在这里插入图片描述

2 桥接模式
  • 将抽象部分与它的实现部分分离,使它们都可以独立地变化,别名:Handle/Body

在这里插入图片描述
在这里插入图片描述

Abstraction:

  • 定义抽象类的接口
  • 维护一个指向 Implementor 类型对象的指针

RefinedAbstration:

  • 扩充由 Abstraction 定义的接口

Implementor(WindowImp)

  • 定义实现类的接口,该接口不一定要与 Abstraction 的接口完全一致;事实上,这两个接口可以完全不同。一般来说,Implementor 接口仅提供基本操作,而 Abstraction 则定义了基于这些基本操作的较高层次的操作

ConcreteImplementor(ImplementorA/B)

  • 实现 Implementor 接口并定义它的具体实现

注:在仅有一个实现的时候,没必要创建一个抽象的Implementor类。这是一种退化的桥接模式。Abstraction 与 implementor 之间一一对应。

手机品牌和软件独立发展,通过桥接模式适配

3 组合模式
  • 将对象组合成树形结构以表示 部分-整体 的层次结构
  • 组合模式使得用户对单个对象和组合对象的使用具有一致性
    在这里插入图片描述

Component(Graphic):

  • 组合中的对象声明接口
  • 在适当的情况下,实现所有类共有接口的缺省行为
  • 声明一个接口用于访问和管理Component的子组件
  • (可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它

Leaf(Rectangle/Line/Text):

  • 在组合中表示叶节点,叶节点没有字节点
  • 在组合中定义图元对象的行为

Composite(Picture)

  • 定义子部件的那些部件的行为
  • 存储子部件
  • 在Component接口中实现与子部件相关的操作

Client:

  • 通过Component接口操纵组合部件的对象
    在这里插入图片描述

效果:

  • 基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去
  • 客户代码中,任何用到基本对象的地方都可以使用组合对象
4 装饰者模式
  • 动态的给一个对象添加一些额外的职责(对核心职责的装饰)。Wrapper
  • 我们可以给某个对象而不是整个类添加一些功能
  • 初始化时的动态,初始化后不可变
  • 避免了静态实现所有功能组合,导致子类急剧增加
    在这里插入图片描述
5 门面模式
  • 为子系统中的一组接口提供一致的界面。是一个高层接口,这个高层接口使得系统更容易使用
  • 通过提出高层接口,使得子系统间依赖程度更低
6 代理模式
  • 为其他对象提供一种代理以控制对这个对象的访问
  • 当之间访问一个对象不便或不符合需求时,为这个实体提供一个替代者

6 行为型模式

1 责任链模式
  • 使得多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系
  • 将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
2 命令模式
  • 将一个请求封装成一个对象,从而使你可用不同的请求对客户端进行参数化
    在这里插入图片描述

  • 命令模式将请求者与执行者分隔开了

3 中介者模式
  • 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其松耦合,而且可以独立地改变它们的交互
  • 迪米特法则
4 备忘录模式
  • 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可将该对象恢复到原先保存的状态(检查点)。别名:Token
    在这里插入图片描述
5 发布订阅模式
  • 定义对象间的一种一对多的依赖关系;当一个对象的状态发生改变时,所有依赖于它 对象都得到通知并被自动更新

  • 使得目标和观察者之间解耦

  • 一下的 Notify 在观察者中
    在这里插入图片描述

  • Subject:观察者,观察者通知所有的订阅者

  • Observer:订阅者,订阅者选择一个观察者进行订阅

  • 可以将 观察者中的 Notify 变成 订阅者中的 listener

6 状态模式
  • 允许一个对象在其内部状态改变时改变它的行为
public class StateContext implement actions {
    private State state;
    public StrategyContext(State state) {
        this.state = state;
    }
    public void action1(..) {
        //在 state 中改变 context 的状态,也就改变了 context 的行为了
        state.action1(this);
    }
}
  • 状态模式和策略模式的区别是:策略模式是客户主动选择算法的。而状态模式的状态转换时固定的,客户端只能选择动作
7 策略模式
  • 定义一系列算法,将他们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化
public class StrategyContext {
    private AbstractStrategy strategy;
    public StrategyContext(AbstractStrategy strategy) {
        this.strategy = strategy;
    }
    public double action(..) {
        //use strategy
    }
}
8 模板方法
  • 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
    在这里插入图片描述
9 访问者模式(双分派,最复杂)
  • 当一个元素接受该访问者时,该元素访问者发送一个包含自身类信息的请求。然后访问者将为该元素执行该操作
  • 双分派
  • 适用于数据结构相对稳定的系统
  • 它把数据结构和作用于结构上的操作之间耦合解开,使得操作集合可以相对自由地演化
    在这里插入图片描述
public class ObjectStructure {
    private IList<Element> elements = new List<Element>();
    public void attach(Element element) {
        elements.add(element);
    }
    public void detach(Element element) {
        elements.remove(element);
    }
    public void accept(Vistor visitor) {
        for (Element e : elements) {
            //第1次分派
            e.accept(visitor);
            //accept 内部进行第2次分派
        }
    }   
}
总结

封装变化使很多行为模式的主题

  • 策略对象封装一个算法
  • 状态对象封装一个与状态相关的行为
  • 中介对象封装对象间的协议
  • 迭代器对象封装访问和遍历一个聚集对象中的各个构件方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值