设计模式介绍
什么是模式\软件设计模式
舒适窗户位置的模式
- 能舒适坐下来
- 能朝向光线
人们在自己的环境中不断发现问题和寻找问题的解决方案的时候,发现一些问题及其解决方案不断变换面孔重复出现,但在这些不同的面孔背后有着共同的本质,这些共同的本质就是模式。
特定问题 -> 解决方案 -> 抽象提炼公共要素 -> 模式 -> 相似问题的解决方案 = 举一反三
模式是建筑行业的延伸
模式是指从某个具体的形式中得到的一种抽象,在特殊的非任意性的环境中,该形式不断地重复出现。
模式 != 某个问题的答案
软件设计模式
- 面向对象软件的开发设计得经验总结
- 一个重要的可重现的面向对象设计方案的命名、解释和评价
关注在特定设计环境中重复出现的设计问题,并为它提供一个解决方案
如MVC模式关注问题是支持用户界面的可变性
解决方案-严格区分职责,将应用程序的核心功能从其用户界面中分离出来用文档记录下来的设计经验,来重用设计知识
这种设计经验是现已存在,经过充分考验的,不是人工发明或者创造
是类和实力层次之上的抽象
一个模式描述几个组件、类和对象,并详细说明他们的职责和关系以及它们之间的合作
所有组件共同解决模式关注的问题,而且通常比单个组件更有效
例如,MVC模式描述了三个合作组件的三元组,每个MVC三元组叶童其他的MVC三元组合作提供一种设计原则的公共词汇和理解
模式名称是对问题及方案需求的简洁描述
设计模式的历史
设计模式的分类
设计原则
设计模式目录
创建性模式(create pattern)
封装动态产生对象的过程和所使用的类的信息,解决系统在创建对象时,抽象化类的实例化过程:
基本原理:
- 怎样创建对象
- 创建哪些对象
- 如何组合和表示这些对象
使系统不依赖于对象的创建过程,只了解抽象类定义的对象接口
- 封装要创建的具体类的信息
- 隐藏这些类(类的实例)被创建和组合的过程
两种创建模式:
类的创建模式 - 使用继承关系,把类的创建延迟到子类
对象的创建模式 - 把对象的创建过程动态地委派给另一个对象
- 封装要创建的具体类的信息
- 隐藏这些类被创建和组合的过程
结构性模式(structural patterns)
考虑如何将组合类和对象构成较大的结构
- 结构性类模式: 使用继承来组合接口和实现
- 结构性对象模式:对象合成实现新功能
对象合成:可以在运行时改变组合
类组合:只能实现静态组合
行为性模式
主要解决算法和对象之间的责任分配
行为模式描述了:
对象或类的模式
它们之间的通信模式
创建性模式
简单工厂模式:类创建模式
- 工厂方法模式的一个特殊实现
- 是学习工程方法模式的准备
- 由一个工厂对象决定创建哪一种产品类的实例
参与者职责
工厂
抽象产品
具体产品
优点:
设计简单
产品类等级结构变化不会影响代工厂类
职责分明,创建和使用部分分开
缺点:
- 工厂类会变得非常负责,形成一个上帝类
- 简单工厂模式使用静态方法,而静态方法无法由子类继承,因此,工厂角色无法形成基于继承的等级结构
以上缺点在工厂方法模式中得到克服
具体使用:
DataFormat
XMLReaderFactory
工厂方法模式
工厂方法模式(Factory Method)
类的创建模式
别名:虚拟构建者(Virtual Constructor)
多态性工厂(Polymorphic Factory)
目的:
定义创建产品对象的工厂接口,让子类决定实例化哪一个类,将实际创建推迟到子类中
“推迟到子类中” - 使用一个类通过子类实例化
要生产A类产品的实例
- 在工厂接口下建立一个专门的工厂(具体工厂)
- 客户端调用具体工厂的实例去生产A类产品的具体产品
- 具体工厂的父类(即工厂接口)完全不知道
- 实例化那个具体工厂
- 生产什么产品
和简单工厂模式区别:多态性
- 工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类
- 核心类-抽象工厂
- 仅负责给出具体工厂子类必须实现的接口,而不接触一个产品类被实例化的细节
可以允许系统在不修改具体工厂角色的情况下引进新的产品
抽象工厂模式
- 所有形态的工厂模式中最为抽象和最具一般性的形态
- 向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象
抽象工厂模式与工厂模式的区别
- 工厂方法模式针对的是一个产品等级结构
- 而抽象工厂模式需要面对多个产品等级结构
目的
在不指明类型的情况下,提供一个创建相联系或相依赖的对象接口
解决方案
思路
用户接口工具箱
按不同的观感(Look-and-feel)定义用户接口工具,如滚动条、窗口、按钮的不同表现和行为
要求在不同观感标准之间提高可移植性结构
参与职责
- 抽象工厂类(AbstractFactory)-(WidgetFactory)
- 具体工厂类 (ConcreteFactory) - (MotifWidgetFactory, PMWidgetFactory)
- 抽象产品类 (AbstractProduct) - (Window, ScrollBar)
- 具体产品类 (ConcreteProduct)
- 定义将被相应的具体工厂类产生的产品对象
- 实现抽象产品类接口
- 客户(client)
- 仅使用由抽象工厂类和抽象产品类声明的接口
协作
- ConcreteFactory类的一个实例一般在运行时产生。它产生有特定实现的产品对象。
要产生不同的产品对象,Client要使用不同的ConcreteFactory - AbstractFactory将产品对象交给ConcreteFactory创建
- ConcreteFactory类的一个实例一般在运行时产生。它产生有特定实现的产品对象。
使用条件
- 系统与如何创建、组合和表示其产品无关
- 系统应由多个产品族之一配置
- 相关对象族需要同时设计和使用
- 应提供产品类库,只提供接口,而不是实现
- 后果:
- 隔离了具体的类
- 使产品族间的转换容易
- 提供了产品间的一致性
- 很难支持新产品种类
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 | 简单工厂:一个工厂生产多种产品 |
单例模式
什么是单例? 孤子模式、单态模式
让类自己负责保存它唯一的实例,这个类可以保证不能再产生别的实例,而且提供获得这个实例的方法
某个类只能有一个实例
它必须自行创建这个实例
它必须自行向整个系统提供这个实例
这个类称为单例类
1 | 用途 |
几种创建单例模式的方法:
1 | // 如果对创建对象的时间无所谓,可以使用这个 |
1 | // synchronized 有性能损耗 |
1 | // 最推荐 |
1 | // 这种方法不推荐,因为枚举一般是常量 |
后果:
有控制地得到唯一的实例。
限制了实例个数,减小了内存空间。
但是不推荐使用这种模式,应该通过容器的限制去使用,比如Spring
多例模式
- 有上限多例
- 单例的推广
- 比如麻将牌的两个骰子
- 无上限多例
- 特点
- 多例类可以有多个实例
- 多例类必须自己创建自己的实例,并管理自己的实例,和向外界提供自己的实例
1 | // 有上限多例 |
1 | 无上限的多例 |
结构性模式
适配器模式
桥接模式
组合模式
装饰模式
门面模式 (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