概述
1. 设计模式
前言:本文为了梳理我的知识脉络,简要笼统的概述了基本的设计模式。如要深入研究不建议以本文作为入门。
1. 为什么要有设计模式
简单来说,为了复用
模式是一种问题的解决思路,它已经适用于一个实践环境,并且可以适用于其它环境。设计模式通常是对于某一类软件设计问题的可重用的解决方案,
将设计模式引入软件设计和开发过程,其 目的就在于要重用软件开发经验。一般模式有4个基本要素:模式名称(pattern name)、问题 (problem)、解决方案(solution)、效果(consequences)。
2. 有哪些设计模式的类型
根据目的分类:
- 创建型:和对象创建有关
- 结构型:处理类和对象的组合
- 行为型:描述类和对象如何交互、如何分配职责
3. 创建型设计模式
在OO中,创建对象没有什么困难,但是如果基于不同的情况创建不同的对象,这个过程就不容易了。创建型模式对类的实例化过程进行了抽象,能够使软件模块做到与对象的创建和组织无关。
3.1 工厂模式
工厂模式的意图是:不直接通过对象的具体实现类,而是通过使用专门的类来负责一组相关联的对象的创建。即定义一个用于创建对象的接口,让子类决定将哪一个类实例化。
这样做的目的是,将类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实例化(创建)哪一个类
【Factory Method 使一个类的实例化延迟到其子类】
工厂模式适用于:
-
当一个类不知道它所必须创建的对象的类的时候。
-
当一个类希望由它的子类来指定它所创建的对象的时候。
-
客户需要清楚创建了哪个对象时候。
工厂模式的UML类图:
代码实现参考:
package DesignPattern.simplefactory;
public class factory {
public static void main(String[] args) {
//假如说我需要一个A产品,那么我就可以通过A的工厂去获取这个对象
ACreater aCreater = new ACreater();
//我外部只知道这是一个Product,但是不知道是不是AProduct
Product product = aCreater.getProduct();
//我去得到它才知道是A产品
System.out.println(product.getName());
}
private static class ACreater implements Creater{
@Override
public Product getProduct() {
return new AProduct();
}
}
private static class AProduct implements Product{
@Override
public String getName() {
return "我是A产品";
}
}
private interface Creater{
Product getProduct();
}
private interface Product{
String getName();
}
}
工厂模式的优势与缺点:
- 在工厂方法模式中,工厂方法用来创建客户需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
- 用户只需关注抽象产品与抽象工厂。
- 创建对象交给具体的工厂来实现。
缺点就是每新加一个产品,就要新加一个对应的具体工厂类。
3.2 抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
抽象工厂模式的适用性分别如下:
-
一个系统要独立于它的产品的创建、组合和表示时。
-
一个系统要由多个产品系列中的一个来配置时。
-
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
-
想提供一组对象而不显示他们的实现过程,只显示他们的接口时。
优缺点:
1.抽象工厂模式的主要优点是隔离了具体类的生成,使得客户不需要知道什么被创建,由于这种 隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为
使用工厂模式 的最大好处是:当同一个产品族中的多个对象被设计成一起工作时,他能够保证客户端始终只使用 同一个产品族中的对象。这对于一些需要根据当前环境来决定其行为的软件来说是一种非常使实用 的设计模式。
2.抽象工厂的缺点是:在添加新产品对象时,难以扩展抽象工厂以便产生新的产品。这是因为 Abstract Factory接口规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口 进行扩展,而这涉及到对Abstract Factory及其所有子类的修改,有些不方便
3.3 建造者模式
将一个复杂对象的构建和他的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建他们。
用户不知道内部的具体构建细节
生成器模式的适用性分别如下:
-
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
-
当构造过程必须允许被构造的对象有不同的表示时。
建造者模式,听名字就应该知道和工厂模式一样,是用来创造对象的。但是建造者和工厂模式的区别就是工厂模式只关注最终的产品,它往往是简单的调用被创建者的构造函数;
而建造者更关心细节,它定义了创建一个复杂对象所需的步骤,而创建者具体的实现类可根据具体的需求,调整创建细节。
比如建造一个人: 抽象建造者把造人的过程分解为: 建造头部,躯杆,四肢, 那么不同的具体建造者可以根据客户需要, 建造出不同的人来: 有的人躯体胖, 有的人手臂长,这是构造函数无法提供的弹性。
3.4 单例模式
确保某个类只有一个实例,且自行实例化,并向整个系统提供这个实例
3.5 原型模式
原型模式指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Prototype模式允许一个对象再次创建另外一个可定制的对象,根本无须知道任何创建的细节。
工作 原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对
象拷贝原型自己来实施创建过程
原型模式的适用性如下:
-
类的实例化是动态的。
-
需要避免使用分层次的工厂类来创建分层次的对象。
-
类的实例对象只有一个或者很少的几个状态组合
原型模式得到了广泛的应用,特别是在创建对象成本较大的情况下(初始化需要占用较长时间,占用太多CPU资源或者网络资源,或者创建对象需要装在大文件),系统如果需要重复利用, 新的对象可以通过原型模式对已经存在对象的属性进行复制并且稍作修改获得。
另外,如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存不多的时候,也可以用原型模式配合备忘录模式来使用。
相反地,如果对象状态变化很大,或者对象占用内存很大那么采用状态模式比原型模式更好,
原型模式的缺点是在实现深层复制的时候需要编写复杂的代码。
4. 结构型模式
结构型模式描述类和对象之间如何进行有效的组织,以形成良好的软件体系结构,
主要的方法是使用继承关系来组织各个类,一个最容易的例子就是如何用多个继承组织两个以上的类,结果产生的类结合了父类所有的属性,结构型模式特别适用于和独立的类库一起工作
4.1 适配器模式
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
适配器模式的适用性如下:
-
对象需要利用现存的并且接口不兼容的类。
-
你需要创建可重用的类以协作其他接口不一定兼容的类。
-
你需要使用若干个现存的子类,但又不想派生这些子类的每一个接口。
4.2 桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化
桥接模式的适用性如下:
-
避免抽象方法和实现方法邦定在一起。
-
类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。
-
对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
-
想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点
桥接模式是把继承关系变成合成/聚合关系。手机可以按照品牌来分类,则有手机品牌M,手机品牌N之分,现在的每个手机都有很多软件,比如通讯录,手机游戏等等,运用桥接模式,可把手机 系统划分为品牌和软件,使他们可以独立的变化。
桥接模式可以从接口中分离实现功能,使得设计更具有扩展性,这样,客户调用方法时根本不需要知道实现的细节。桥接模式的缺陷是:抽象类和实现类的双向连接使得运行速度减慢
4.3 组合模式
组合模式的意图是将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式对单个对象和组合对象的使用具有一致性。
组合模式的适用性如下:
-
想表示对象的部分-整体层次结构。
-
希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
组合模式使得用户对单个对象和组合对象的适用具有一致性,可让客户可以一致的适用组合结构和单个对象。比如文件系统中有文件,也有文件夹(其实是特殊的文件),文件和文件夹组合在 一起,可以成为一个更大的文件夹。在用户看来,不管是文件,还是文件夹,还是更目录,对它们的操作都是一致的(当然试图把一个文件夹放到文件下面是不容许的)。这就是说整体和部分具有一致的接口。
组合模式可以清楚地定义分层次的复杂对象。表示对象的全部或者部分层次,使得增加新部件也更容易,并且它的结构也是动态的,提供了对象管理的灵活接口。
组合模式的缺陷是使得设计变得更加抽象,对象的商业规则如果很复杂,则实现组合模式有很大的挑战性,并不是所有的方法都与叶部件子类有关联
4.4 装饰模式
动态的给一个对象增加职责,即在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰模式的适用性如下:
-
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。处理那些可以撤消的职责。
-
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
4.5 外观模式
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
外观模式的适用性如下:
-
当要为一个复杂子系统提供一个简单接口时
-
客户程序与抽象类的实现部分之间存在着很大的依赖性。
-
当需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。
外观模式为子系统提供了一个更高层次、更简单的接口,从而降低了子系统的复杂度和依赖。 外观对象隔离了客户和子系统对象,从而降低了耦合度。当子系统中的类进行改变时,客户端不会 像以前一样受到影响。这使得子系统更易于使用和管理。外观模式的劣势就是限制了客户的自由, 减少了可变性
4.6 享元模式
运用共享技术有效的支持大量细粒度的对象。系统只是用少量 的对象,而这些对象都相近,状态变化很小,对象使用次数增多
享元模式/轻量级模式的适用性如下:
- 一个应用程序使用了大量的对象。
- 完全由于使用大量的对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态。
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值
享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相 互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象
Flyweight模式需要认真考虑如何能细化对象,以减少对象的数量,从而减少存留对象在内存或 其他存储设备中的占用量。然而,此模式需要维护大量对象的外部状态,如果外部对象的数据量 大,传递查找计算这些数据会变得非常复杂。当外部和内部状态很难分清时,不宜采用flyweight模 式。
4.7 代理模式
为其他对象提供一个代理或地方以控制对这个对象的访问。
当客户向Proxy 对象第一次提出请求时,Proxy实例化真实的对象,并且将请求传给它,以后所有客户的请求都由 Proxy传递给封装了的对象。
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一些可以 使用Proxy模式的常见情况:
- 远程代理(Remote Proxy):为一个对象在不同的地址空间提供局部代表。
- 虚代理(Virtual Proxy):根据需要创建开销很大的对象。
- 保护代理(Protection Proxy):控制对原始对象的访问。保护代理用于对象应该有不同的访 问权限的时候。
- 智能指引(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作。它 的典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它;当第 一次引用一个持久对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确 保其他对象不能改变它
当对象在远程机器上,要通过网络来生成时,速度可能会比较慢,这时,用代理模式(Remote Proxy)可以掩蔽对象由网络生成的过程,系统速度会加快,对于大图片的加载,Virtual Proxy可以 让加载在后台进行,前台用的Proxy对象使得整体运行速度得到优化。Protection Proxy可以验证对 真实对象的引用权限。
代理模式的缺陷是请求的处理速度会变慢,并且实现Proxy需要额外的工作。
5. 行为型模式
行为型设计模式描述类和对象之间如何交互以及如何分配职责,实际上它所牵涉的不仅仅是类 或者对象的设计模式,还有他们之间的通信模式。
5.1 职责链模式
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它
职责链模式的适用性如下:
- 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
- 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。可处理一个请求的对 象集合应被动态指定。
责任链模式的关键点在于把请求的处理者连成一条链,一个处理者可以处理当前请求,也有权决定 是否沿着链朝上传递请求
责任链模式可以减少对象的连接,为对象责任分配增加了很大的灵活性。该模式可以简化对象之间的连接,他们只需要知道一个后继者就行了,可以很方便的增加或修改处理者。树状责任链能够提供更灵活的技巧,但缺点是信息在树中容易迷失
5.2 命令模式
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作
命令模式的适用性如下:
- 当你需要与动作有关的对象作为参数。
- 在不同的时刻指定、排列和执行请求。
- 你需要支持取消,修改和保存日志或处理事务(事务包括大量修改的数据)功能。
- 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。
- 用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务(transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。模式提供了对事务进行建模的方法。
命令模式分离了接收请求的对象和实现处理请求工作的对象,这样,已经存在的类可以保持不变。是的增加新类的工作变得简单。此外命令模式还可以分离用户界面和业务对象,降低系统的耦合度。
命令模式的最主要的缺点是:类的数量增加了,系统变得更复杂,程序的调试工作也相应的变得困难。
5.3 解释器模式
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子
解释器模式的适用性如下:
- 当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
- 该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。
- 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它 们转换成另一种形式。
解释器模式的作用很强大,它使得改变和扩展文法很容易,实现文法也变得简单明了,很多编译器,包括文本编辑器,网页浏览器都用到了解释器模式。
解释器模式的缺陷在于:因为文句分析成树结构,解释器需要递归访问它,所以效率会受到影响,编译整个工程耗时比较长。
5.4 迭代器模式
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示
迭代器模式的适用性如下:
- 需要遍历访问聚集中的对象而不能暴露聚集的内部结构。
- 允许对聚集的多级遍历访问而不会相互受到影响。
- 提供一个一致的接口来遍历访问集中不同的结构。
迭代器支持在聚集中移动游标,使得访问聚合中的元素变得简单,简化了聚集的接口,封装了聚合的对象。迭代器模式还可以应用于树形结构的访问,程序不需要从头行代码查找相应的位置, 可控制到从子集开始查找,对于加快程序的运行速度有很重要的作用。缺点是聚合密切相关,增加了耦合,但是将这种耦合定义在抽象类,可以解决这种问题
5.5 中介者模式
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
中介者模式的适用性如下:
- 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
- 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。想定制一个分布在多个类中的行为,而又不想生成太多的子类。
中介者模式的优点:把中介者作为一个独立的概念,可使对象之间的关系转为对象和中介者之间的关系,可站在一个宏观的角度看问题。
中介者模式的缺点:中介者集中控制关系,使得中介者变得复杂。
5.6 备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
备忘录模式的适用性如下:
- 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
- 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装 性
5.7 观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
观察者模式的适用性如下:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以 使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
观察者模式抽象了被观察者和观察者对象的连接,作用在于解耦, 就是让耦合的双方都依赖于抽象而不是具体,从而使得各部分的变化都不会影响到对方,容易增加新的观察者对象
观察者模式的缺陷是:对象间的关系难以理解,在某种情况下会表现低效能。
5.8 状态模式
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了 它的类
状态模式的适用性如下:
- 对象的行为依赖于它的状态(属性),并且它必须可以根据它的状态而改变它的行为。
- 操作很多部分都带有与对象状态有关的大量条件语句
状态模式在对象内保存特定的状态,并且就不同的状态履行不同的行为,它使状态的变化显得清晰了,也很容易创建对象的新状态。状态模式在工作流或游戏等各种系统中有大量的应用
5.9 策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化
策略模式的适用性如下:
- 多个类的分别只在于行为不同
- 需要对行为算法做很多变动
- 客户不需要知道算法使用的数据
策略模式提供了替代派生的子类,并定义类的每个行为,把容易发生变化的算法独立出来,易于扩展,系统变得更加灵活。应用策略模式会产生很多子类,这符合高内聚的责任分配模式,类增多了,使系统难度加大
5.10 模板方法模式
定义一个操作中算法的骨架,以将一些操作延缓到子类中实现,模板方法让子类重新定义一个算法的某些步骤,而无需改变算法结构。
模板方法模式的适用性如下:
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。各子类中公共的行为应 被提取出来并集中到一个公共父类中以避免代码重复。
- 控制子类扩展
模板方法模式在一个类中形式化的定义算法,而由他的子类实现细节的处理,模板方法模式的 优势是把公共的代码或不变的行为放在超类/模版中, 以隔离变化和不变,使不便的部分可复用。
模 板方法的特点在于:每个不同的实现都需要定义一个子类,这也符合高内聚的设计模式。
5.11 访问者模式
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
访问者模式的适用性如下:
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体 类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
Visitor模式使得增加新的操作变得容易,它可以收集有关联的方法,而分离没有关联的方法, 特别适用于分离因为不同原因而变化的事物,但是Visitor模式常常要打破对象的封装,visitor同 element需要达成某些共识
模版中, 以隔离变化和不变,使不便的部分可复用。
模 板方法的特点在于:每个不同的实现都需要定义一个子类,这也符合高内聚的设计模式。
5.11 访问者模式
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
访问者模式的适用性如下:
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体 类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
Visitor模式使得增加新的操作变得容易,它可以收集有关联的方法,而分离没有关联的方法, 特别适用于分离因为不同原因而变化的事物,但是Visitor模式常常要打破对象的封装,visitor同 element需要达成某些共识
最后
以上就是腼腆冰淇淋为你收集整理的设计模式学习1. 设计模式的全部内容,希望文章能够帮你解决设计模式学习1. 设计模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复