设计模式总结:创建型模式
创建型模式
- 类模式
- 工厂方法模式
- 对象模式
- 抽象工厂模式
- 建造者模式
- 原型模式
- 单例模式
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
- 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
1 简单工厂模式 Simple Factory
1.1 定义
- 简单工厂模式(Simple Factory Pattern),又称为静态工厂方法(Static Factory Method)模式
- 根据参数的不同返回不同类的实例
- 专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类
包含如下角色
- Factory 工厂角色
- Product 抽象产品角色
- ConcreteProduct 具体产品角色
1.2 模式分析
-
将对象的创建和对象本身业务处理分离可以降低系统的耦合度 ,使得两者修改起来都相对容易。
-
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
-
优点
- 实现了对责任的分割,提供专门的工厂类用于创建对象
- 客户端无需知道所创建的具体产品类目,只需要知道产品类对应的参数即可
- 引入配置文件,可以在不修改客户端代码的情况下更换和新增新的具体产品类
-
缺点
- 工厂类集合了所有产品创建逻辑,一旦不能工作,整个系统都会被影响
- 会增加类的个数
- 拓展困难,一旦添加新产品,就不得不修改工厂逻辑(违背了开闭原则)
-
适用
- 工厂类负责创建的对象比较少(不会导致工厂的逻辑太复杂)
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心
-
简化
-
可以把静态工厂方法写到抽象产品类中
-
1.3 例子
-
根据不同的权限等级创建不同的用户对象,不同的用户对象拥有不同的操作权限
-
JDK日期格式化类
-
JDK加密技术
2 工厂方法模式 Factory Method
2.1 模式动机
考虑简单工厂的不足,即要增加新产品时,除了增加一个新的具体产品类之外,还需要修改工厂类的代码,这就使得整个设计在一定程度上违反了“开闭原则 ”
修改:不再设计一个工厂类来统一负责所有产品的创建,而是将具体类的创建过程交给专门的工厂子类去完成
2.2 定义
- 工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式
2.3 模式分析
-
在工厂方法模式中,核心的工厂类不再负责所有产 品的创建,而是将具体创建工作交给子类去做。因此,工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。(符合开闭原则)
-
在定义工厂和产品时都必须使用抽象层,如果需要更换产品类,只需要更换对应的工厂即可,其他代 码不需要进行任何修改。
-
优点
- 用户只需关心所需产品对应的工厂,无需关心创建细节,甚至无需知道具体产品类名
- 工厂可以自主确定如何创建何种产品对象,如何创建对象的细节完全封装在工厂内部
- 引入新产品时,无需修改抽象工厂和产品,无需修改客户端,也无需修改其他具体工厂和产品,只需要添加一个具体产品和工厂类即可
-
缺点
- 引入新产品时,需要引入具体工厂类,一定程度上增加了系统的复杂度
- 客户端中需要使用对抽象层的定义,增加系统的抽象性和理解难度,需要用到反射等技术
-
适用
- 一个类不知道它所需要的对象的类
- 一个类通过其子类来指定创建哪个对象
- 将创建对象的任务委托给多个工厂子类中的一个,在使用时动态指定
2.4 例子
3 抽象工厂模式 Abstract Factory
3.1 模式动机
- 工厂方法中,一个具体工厂对应一种具体产品
- 有时候我们为需要一个工厂可以提供多个产品对象,而不是单一的产品对象
3.2 定义
- 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式
引入两个概念
- 产品等级结构:即产品的继承结构,如抽象电视机是父类,具体品牌的电视机是其子类
- 产品族:指由同一个工厂生产的,位于不同产品等级结构中的一组产品
包含以下角色:
- AbstractFactory
- ConcreteFactory
- AbstractProduct
- Product
3.3 模式分析
-
优点
- 隔离了具体类的生成,使得客户端不需要知道什么被创建
- 所有的具体工厂都实现了抽象工厂中的公共接口,只需要替换具体工厂,就可以改变整个软件系统的行为
- 高内聚、低耦合
- 保证客户端始终使用同一个产品族的对象
- 增加新的具体工厂和产品族很方便,无需修改已有系统(开闭原则)
-
缺点
- 添加新的产品时,很难扩展抽象工厂来生成新的产品
- 新增新的产品族和工厂容易,新增新的产品等级结构难。即开闭原则的倾斜性
-
适用
- 一个系统不应当依赖产品类实例如何被创建、组合和表达的细节(对所有工厂模式都很重要)
- 系统中有多个产品族,且每次只用一个
- 属于同一个产品族的产品将在一起使用
- 系统提供一个产品族的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现
3.4 例子
-
一个电器工厂可以生产多个类型的电器,有多个工厂
-
针对不同的数据库,提供Connector和Statement
4 工厂模式退化
5 建造者模式 Builder
建造者模式(Bulider模式)详解 (biancheng.net)
5.1 模式动机
- 存在一些复杂的对象,有多个组成部分,如汽车包括轮胎、方向盘等,而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车
- 建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象
- 建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式。
5.2 定义
- 建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
包含以下角色
- Builder:抽象建造者
- ConcreteBuilder:具体建造者
- Director:指挥者
- Product:产品角色
5.3 模式分析
-
引入指挥者类,一方面它隔离了客户与生产过 程;另一方面它负责控制产品的生成过程。
-
在客户端代码中,无需关心产品对象的具体组装过程,只需确定具体的建造者类型即可
-
优点
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,用户使用不同的具体建造者即可得到不同的产品对象。
- 可以更加精细地控制产品的创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
-
缺点
- 如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
-
适用
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
-
简化
- 省略抽象建造者角色:如果系统中只需要一个具体建造 者的话,可以省略掉抽象建造者。
- 省略指挥者角色:在具体建造者只有一个的情况下,如 果抽象建造者角色已经被省略掉,那么还可以省略指挥 者角色,让Builder角色扮演指挥者与建造者双重角色。
5.4 例子
建造者模式可以用于描述KFC如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和 饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求, 一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客
5.5 对比
通过前面的学习,我们已经了解了建造者模式,那么它和工厂模式有什么区别呢?
- 建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。
- 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
- 关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。
- 建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。
6 原型模式 Prototype
6.1 模式动机
- 使用原型模式来复制一个对象自身, 从而克隆出多个与原型对象一模一样的对象。
- 在软件系统中,有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明 所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。
6.2 定义
-
原型模式(Prototype Pattern):原型模式是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过复 制这些原型创建新的对象。
-
原型模式允许一个对象再创建另外一个可定制的对象,无须知道任何创建的细节。
原型模式包含如下角色:
- Prototype:抽象原型类
- ConcretePrototype:具体原型类
- Client:客户类
6.3 模式分析
-
一个类包含一些成员对象,在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可以分为两种形式:深克隆和浅克隆
-
JAVA中有clone方法
- 所有的Java 类都继承自
java.lang.Object
,而Object类提供一个 clone()方法,可以将一个Java对象复制一份 - 能够实现克隆的Java类必须实现一个标识接口Cloneable, 表示这个Java类支持复制。如果一个类没有实现这个接 口但是调用了clone()方法,Java编译器将抛出一个 CloneNotSupportedException异常。
- 对任何的对象x,都有
x.clone()!=x
,即克隆对象与 原对象不是同一个对象。 - 对任何的对象x,都有
x.clone().getClass()==x.getClass()
,即克隆对象与原 对象的类型一样。 - 如果对象x的equals()方法定义恰当,那么
x.clone().equals(x)
应该成立。
- 所有的Java 类都继承自
-
优点
- 使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例 的创建效率。
- 可以动态增加或减少产品类。
- 原型模式提供了简化的创建结构。
- 可以使用深克隆的方式保存对象的状态
-
缺点
- 需要为每一个类配备一个克隆方法,对已有的类进行改造时,不一定是件容易的事, 必须修改其源代码,违背了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码
-
适用
- 创建新对象成本较大
- 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象 得到新实例可能比使用构造函数创建一个新实例更加方便
-
扩展
-
相似对象的复制
很多情况下,复制所得到的对象与原型对象并不是完全相同的,它们的某些属性值存在异同。通过原型模式获 得相同对象后可以再对其属性进行修改,从而获取所需对象。
6.4 例子
- 原型模式应用于很多软件中,如果每次创建一个对象要花大量时间,原型模式是最好的解决方案。很多软件提 供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就是原型模式的应用
- 在Spring中,用户也可以采用原型模式来创建新的bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。