0%

Design Pattern

设计模式介绍

什么是模式\软件设计模式

舒适窗户位置的模式

  1. 能舒适坐下来
  2. 能朝向光线

人们在自己的环境中不断发现问题和寻找问题的解决方案的时候,发现一些问题及其解决方案不断变换面孔重复出现,但在这些不同的面孔背后有着共同的本质,这些共同的本质就是模式。

特定问题 -> 解决方案 -> 抽象提炼公共要素 -> 模式 -> 相似问题的解决方案 = 举一反三

模式是建筑行业的延伸

模式是指从某个具体的形式中得到的一种抽象,在特殊的非任意性的环境中,该形式不断地重复出现。

模式 != 某个问题的答案

软件设计模式

  • 面向对象软件的开发设计得经验总结
  • 一个重要的可重现的面向对象设计方案的命名、解释和评价
  1. 关注在特定设计环境中重复出现的设计问题,并为它提供一个解决方案
    如MVC模式关注问题是支持用户界面的可变性
    解决方案-严格区分职责,将应用程序的核心功能从其用户界面中分离出来

  2. 用文档记录下来的设计经验,来重用设计知识

    这种设计经验是现已存在,经过充分考验的,不是人工发明或者创造

  3. 是类和实力层次之上的抽象
    一个模式描述几个组件、类和对象,并详细说明他们的职责和关系以及它们之间的合作
    所有组件共同解决模式关注的问题,而且通常比单个组件更有效
    例如,MVC模式描述了三个合作组件的三元组,每个MVC三元组叶童其他的MVC三元组合作

  4. 提供一种设计原则的公共词汇和理解
    模式名称是对问题及方案需求的简洁描述

设计模式的历史

设计模式的分类

设计原则

设计模式目录

创建性模式(create pattern)

封装动态产生对象的过程和所使用的类的信息,解决系统在创建对象时,抽象化类的实例化过程:

基本原理:

  • 怎样创建对象
  • 创建哪些对象
  • 如何组合和表示这些对象

使系统不依赖于对象的创建过程,只了解抽象类定义的对象接口

- 封装要创建的具体类的信息
- 隐藏这些类(类的实例)被创建和组合的过程

两种创建模式:
类的创建模式 - 使用继承关系,把类的创建延迟到子类
对象的创建模式 - 把对象的创建过程动态地委派给另一个对象

- 封装要创建的具体类的信息
- 隐藏这些类被创建和组合的过程

结构性模式(structural patterns)

考虑如何将组合类和对象构成较大的结构

  1. 结构性类模式: 使用继承来组合接口和实现
  2. 结构性对象模式:对象合成实现新功能

对象合成:可以在运行时改变组合
类组合:只能实现静态组合

行为性模式

主要解决算法和对象之间的责任分配

行为模式描述了:
对象或类的模式
它们之间的通信模式

创建性模式

简单工厂模式:类创建模式

  • 工厂方法模式的一个特殊实现
  • 是学习工程方法模式的准备
  • 由一个工厂对象决定创建哪一种产品类的实例

参与者职责

  • 工厂

  • 抽象产品

  • 具体产品

优点:
设计简单
产品类等级结构变化不会影响代工厂类
职责分明,创建和使用部分分开

缺点:

  • 工厂类会变得非常负责,形成一个上帝类
  • 简单工厂模式使用静态方法,而静态方法无法由子类继承,因此,工厂角色无法形成基于继承的等级结构

以上缺点在工厂方法模式中得到克服

具体使用:

DataFormat

XMLReaderFactory

工厂方法模式

工厂方法模式(Factory Method)

类的创建模式

别名:虚拟构建者(Virtual Constructor)
多态性工厂(Polymorphic Factory)

目的:

定义创建产品对象的工厂接口,让子类决定实例化哪一个类,将实际创建推迟到子类中

“推迟到子类中” - 使用一个类通过子类实例化

要生产A类产品的实例

  • 在工厂接口下建立一个专门的工厂(具体工厂)
  • 客户端调用具体工厂的实例去生产A类产品的具体产品
  • 具体工厂的父类(即工厂接口)完全不知道
    • 实例化那个具体工厂
    • 生产什么产品

和简单工厂模式区别:多态性

  • 工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类
  • 核心类-抽象工厂
  • 仅负责给出具体工厂子类必须实现的接口,而不接触一个产品类被实例化的细节

可以允许系统在不修改具体工厂角色的情况下引进新的产品

抽象工厂模式

  • 所有形态的工厂模式中最为抽象和最具一般性的形态
  • 向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象

  • 抽象工厂模式与工厂模式的区别

    • 工厂方法模式针对的是一个产品等级结构
    • 而抽象工厂模式需要面对多个产品等级结构
  • 目的
    在不指明类型的情况下,提供一个创建相联系或相依赖的对象接口

解决方案

  1. 思路
    用户接口工具箱
    按不同的观感(Look-and-feel)定义用户接口工具,如滚动条、窗口、按钮的不同表现和行为
    要求在不同观感标准之间提高可移植性

  2. 结构

  3. 参与职责

    • 抽象工厂类(AbstractFactory)-(WidgetFactory)
    • 具体工厂类 (ConcreteFactory) - (MotifWidgetFactory, PMWidgetFactory)
    • 抽象产品类 (AbstractProduct) - (Window, ScrollBar)
    • 具体产品类 (ConcreteProduct)
      • 定义将被相应的具体工厂类产生的产品对象
      • 实现抽象产品类接口
    • 客户(client)
      • 仅使用由抽象工厂类和抽象产品类声明的接口
  4. 协作

    • ConcreteFactory类的一个实例一般在运行时产生。它产生有特定实现的产品对象。
      要产生不同的产品对象,Client要使用不同的ConcreteFactory
    • AbstractFactory将产品对象交给ConcreteFactory创建
  5. 使用条件

    • 系统与如何创建、组合和表示其产品无关
    • 系统应由多个产品族之一配置
    • 相关对象族需要同时设计和使用
    • 应提供产品类库,只提供接口,而不是实现
  • 后果:
    • 隔离了具体的类
    • 使产品族间的转换容易
    • 提供了产品间的一致性
    • 很难支持新产品种类

Builder模式

产品的内部表象

一个产品常有不同的组成成分(零件)

如汽车,有很多部件:车轮 方向盘 发动机还有各种小零件等等

这些零件有可能是对象,也有可能不是对象

它们通常叫做内部表象

不同的产品可以有不同的内部表象

解决方案:

思路

将复杂对象的部件内部表象和复杂对象分开来

同样的构建过程可以创建不同的表示

用户不知道具体构建细节, 只通过指定对象的类型和内容构建它们

建造模式使客户端不需要知道:
所生成的产品对象有哪些部件
每个产品的对应零件彼此有何不同
是怎么建造出来的
怎样组成产品

  • 结构

  • 参与者

    • 具体建立者
    • 指导者
    • 产品
    • 客户端

一般来说一个产品类一个builder

协作:

Client产生Director对象,用想要的Builder对象配置它
Director在凡要建造产品的某一部分的时候都通知Builder
Builder处理从Director来的请求,将要建造的这一部分加入到产品中去
Client从Bulider回溯产品

使用条件:

产品对象有复杂的内部结构

产品对象的属性相互依赖。一个属性必须在另一属性被赋值之后才可以被赋值

在对象创建过程中用到创建过程中不易得到的系统中其他对象

在产品的属性没有确定之前,产品对象不能使用。属性赋值和使用时分步骤进行的

生成一个复杂对象的算法独立于建造对象和集成它们的部分

构造过程允许构造对象有不同的表示

例子:

StringBuilder

原型模式

工作原理:

将一个原型对象传给发动创建的对象
原型对象实例指明了创建对象的类
发动创建的对象请求原型对象拷贝它们自己来实施创建
一个对象创建另一个可定制对象,无需知道如何创建的细节

很多语言都自身支持这个模式,比如Java

Java中的Object类含有clone的方法

对任何的对象X, 都有X.clone() != X, X.getClass() == X.clone().getClass()

克隆的对象不能是原来的对象,但是类型必须相同

五个模式的不同

1
2
3
4
5
简单工厂:一个工厂生产多种产品
工厂方法:工厂的分厂生产不同产品
抽象工厂:工厂的每个分厂生产不同品牌的产品族
建造者:工厂的分厂制造零件,总厂负责装配
原型:复印商店

单例模式

什么是单例? 孤子模式、单态模式

让类自己负责保存它唯一的实例,这个类可以保证不能再产生别的实例,而且提供获得这个实例的方法

  • 某个类只能有一个实例

  • 它必须自行创建这个实例

  • 它必须自行向整个系统提供这个实例

这个类称为单例类

1
2
3
4
5
6
7
8
用途
资源管理:
打印机只能有一个 Printer Spooler ——避免两个打印作业间时输出到打印机中。
只应有一个软件负责管理传真卡——避免出现两份传真作业同时传到传真卡中的情况。
通信端口管理——避免一个通信端口同时被2个请求同时调用。
记录网站来访人数的部件
记录软件系统内部事件、出错信息的部件
对系统的表现进行检查的部件等。

几种创建单例模式的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 如果对创建对象的时间无所谓,可以使用这个
public class Singleton {
private Singleton() {
System.out.println("Singleton is create"); //创建单例的过程可能会比较慢
}

private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}

public static void createString(){ //这是模拟单例类扮演其他角色
System.out.println("createString in Singleton");
}
}
1
2
3
4
5
6
7
8
9
10
11
// synchronized 有性能损耗
public class LazySingleton {
private LazySingleton(){
System.out.println("LazySingleton is create"); }
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance() {
if (instance==null)
instance=new LazySingleton();
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
// 最推荐
public class StaticSingleton {
private StaticSingleton(){
System.out.println("StaticSingleton is create");
}
private static class SingletonHolder {
private static StaticSingleton instance = new StaticSingleton();
}
public static StaticSingleton getInstance() {
return SingletonHolder.instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 这种方法不推荐,因为枚举一般是常量
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething() {
System.out.println("complete singleton");
}
};

public static MySingleton getInstance() {
return Singleton.INSTANCE;
}
}

后果:
有控制地得到唯一的实例。
限制了实例个数,减小了内存空间。

但是不推荐使用这种模式,应该通过容器的限制去使用,比如Spring

多例模式

  • 有上限多例
    • 单例的推广
    • 比如麻将牌的两个骰子
  • 无上限多例
  • 特点
    • 多例类可以有多个实例
    • 多例类必须自己创建自己的实例,并管理自己的实例,和向外界提供自己的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 有上限多例
public class Die
{
private static Die die1 = new Die();
private static Die die2 = new Die();

private Die() {

}

public static Die getInstance(int whichOne) {
if (whichOne == 1) {
return die1;
} else {
return die2;
}
}
}
1
2
无上限的多例
比如:国际化支持(不确定到底有多少语言和区域)

结构性模式

适配器模式

桥接模式

组合模式

装饰模式

门面模式 (Facade)

门面模式在四人帮书中讲述它的意图就是“Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.” 另外一本参考书则直接使用“简化”两字描述这一模式。Coursera的软件架构这课上描述也不错:A facade is a wrapper class that encapsulate the subsystem in order to hide the subsystem’s complexity. 用我自己的语言来说,门面模式就是直接满足客户端的需求,即使用一个新的类去集成子系统的功能,从而使客户端不需要知道子系统的功能达到目的.

例子:

四人帮设计模式:编译器的一个compile()方法里包含了scanner和parser.Parse()

享元模式

代理模式

定义:

四人帮书:One reason for controlling access to an object is to defer the full cost of its creation and initialization until we actually need to use it.

Coursera的软件架构: The proxy acts as a simplified or lightweight version of the original object. A proxy object is still able to accomplish the same tasks, but may delegate requests to the original object to achieve them.

个人觉得,代理并不是真的像现实生活一样去帮你代理做事,而是让请求先经过代理,通过代理再在适当得时候调用真正的对象的方法。

应用场景:

使用动态代理,可以为所有的类增加Cache功能

职责链模式

步步为营

职责分明,将请求放到链子上,直到有一个实例对象可以处理这件事情

命令模式

封装命令

命令发出者 命令传递者 命令接收者

其中每个命令是一个对象,统一命令接口,命令通过传递着注册,执行在与调用命令接收者

迭代模式

遍历容器

模版模式

模式中的模式


Reference

炼数成金-设计模式