创建型模式关注对象的创建过程,将对象的创建和使用分离,在使用对象时无需关心对象的创建细节,从而降低系统的耦合度。
创建型模式通过采用不同的解决方案来回答三个问题
- 创建什么
- 由谁创建
- 何时创建
单例模式
单例模式:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
单例模式有三个要点
- 一个类只能有一个实例
- 它必须自行创建
- 它自行向整个系统提供这个实例
饿汉式单例 和 懒汉式单例
饿汉式单例
在定义静态变量的时候实例化单例类,因此在类加载时就已经创建了单例对象
懒汉式单例
懒汉式单例在第一次调用 「getInstance」方法时实例化,在类加载时并不自行实例化,这种技术又称为延迟加载技术
对比
- 饿汉式单例在类被加载时就将自己实例化,优点在于无需考虑多线程访问的问题,可以确保实例的唯一性
- 从调用速度和反应时间上,饿汉式单例在一开始就得以创建,优于懒汉式单例
- 从资源利用效率上,饿汉式单例无论系统在运行时是否需要使用该单例对象,该类加载时都会创建
Initialization on Demand Holder(IoDH)
Initialization on Demand Holder(IoDH)按需实例化
- 静态单例对象没有作为类的成员变量直接实例化,因此加载时不会被实例化
- 第一次调用 「getInstance」时,将加载内部类的实例化方法,初始化实例成员变量
单例模式的优点
- 单例模式提供了对唯一实例的受控访问
- 由于在系统内存中只存在一个对象,因此可以节约系统资源。对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能
- 允许可变数目的实例。基于单例模式,开发人员可以进行扩展,使用与控制单例对象相似的方法来获得指定个数的实例对象,既节省系统资源,又解决了优于单例对象共享过多有损性能的问题
单例模式的缺点
- 单例模式没有抽象层,因此在扩展上有很大的困难
- 单例类的职责过重,在一定程度上违背了单一职责原则
- 现在很多面向对象语言的运行环境提供了自动垃圾回收技术,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失
单例模式的适用场景
- 系统只需要一个实例对象
- 客户调用类的单个实例只允许使用一个公共访问点
简单工厂模式
简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类
因为在简单工厂模式中用于创建实例的方法是静态方法,所以简单工厂模式又被称为静态工厂方法模式,属于创建型模式
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建的细节
简单工厂模式包括三个角色
- 工厂角色:即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑
- 抽象产品角色:它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法
- 具体产品角色:它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例
与一个对象相关的职责通常有三类:
- 对象本身所具有的职责
- 创建对象的职责
- 使用对象的职责
对象本身的职责:对象自身所具有的一些数据和行为,可通过一些公开的方法来实现其职责
所有的工厂模式都强调一点:两个类 A 和 B 之间的关系应该仅仅是 A 创建 B 或者 A 使用 B,而不能两种关系都有
将对象的创建和使用分离,也使得系统更加符合单一职责原则,有利于对功能的复用和系统的维护
简单工厂模式的优点
- 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例。简单工厂模式实现了对象创建和使用的分离
- 客户端无需知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数菊科
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性
简单工厂模式的缺点
- 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都会受到影响
- 使用简单工厂模式势必会增加系统中类的个数,增加系统的复杂度和理解难度
- 系统扩展困难。一旦增加新产品,就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构
简单工厂模式的适用场景
- 工厂类负责创建的对象比较少
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心
工厂方法模式
简单工厂模式只提供一个工厂类,该工厂类处于对产品类初始化的中心位置,需要知道每个产品对象的创建细节
- 有新产品加入时,必须修改工厂类,在其中加入业务逻辑,违背开闭原则
- 工厂类职责过重,业务逻辑复杂,具体产品和工厂类耦合度高,影响系统的灵活性和扩展性
工厂方法模式:定义一个用于创建对象的接口,让子类决定将哪一个类实例化
工厂方法模式,又称工厂模式、虚拟构造器模式或多态工厂模式,属于一种创建型模式
工厂方法模式包含四种角色
- 抽象产品(Product):定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类
- 具体产品(ConcreteProduct):它实现了抽象产品接口,某个类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应
- 抽象工厂(Factory):在抽象工厂中,声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现这个接口
- 具体工厂(ConcreteFactory):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例
工厂方法模式的优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键
- 在系统中加入新产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,只需要添加一个具体工厂和具体产品就可以了
工厂方法模式的缺点
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度
工厂方法模式的适用场景
- 客户端不知道其所需要的对象的类
- 抽象工厂类通过其子类来指定创建哪个对象
抽象工厂模式
抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式和工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。抽象工厂模式又称为 Kit 模式,它是一种对象的创建型模式
抽象工厂模式结构图包含 4 个角色
- 抽象工厂(AbstractFactory),它声明了一组用于创建一组产品的方法,每个方法对应一种产品
- 具体工厂(ConcreteFactory),它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每种产品都位于某个产品等级结构中
- 抽象产品(AbstractProduct),它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法
- 具体产品(ConcreteProduct),它定义具体工厂生产的具体产品对象,实现在抽象产品接口中声明的业务方法
开闭原则的倾斜性:在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为开闭原则的倾斜性
开闭原则要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的,对于涉及多个产品族和多个产品等级结构的系统,其功能增强包括两方面:
- 增加产品族:对于增加新的产品族,抽象工厂模式很好的支持了开闭原则,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无需做任何修改
- 增加新的产品等级结构。对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了开闭原则
抽象工厂模式的优点
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
- 增加新的产品族很方便,无需修改已有系统,符合开闭原则
抽象工厂模式的缺点
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无需关心对象的创建过程,将对象的创建和使用解耦
- 系统中有多于一个的产品族,而每次只使用其中某一个产品族
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
- 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构
原型模式
原型模式:使用原型实例指定创建对象的类型,并且通过克隆这些原型创建新的对象。属于一种对象创建型模式
原型模式的工作原理很简单:将一个原型对象传给要发动创建的对象,这个要发动创建的对象通过请求原型对象克隆自己来实现创建过程
在原型模式结构图中包含三个角色
- 抽象原型类(Prototype),它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类
- 具体原型类(ConcretePrototype),它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象
- 客户类(Client),让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或者通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象
原型管理器是将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得
原型模式的优点
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
- 扩展性较好
- 原型模式提供了简化的创建结构
- 可以使用深克隆的方式保存对象的状态
原型模式的缺点
- 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部。当对已有的类进行改造时,需要修改源代码,违背开闭原则
- 在实现深克隆时需要编写较为复杂的代码
原型模式的适用场景
- 创建新对象的成本较大
- 如果系统要保存对象的状态,而对象的状态变化很小
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或者很少的几个组合状态
建造者模式
建造者模式是较为复杂的创建型模式,它将客户端和包含多个组成部分的复杂对象的创建过程分离
建造者模式:将一个复杂对象的构建与它的表示分离。使得同样的构建过程可以创建不同的表示,是一种对象创建型模式
在建造者模式结构中包含四个角色
-
抽象建造者(Builder),它为创建一个产品 Product 对象的各个部件指定抽象接口。在该接口中一般声明两类方法
- buildPartX(),用于创建复杂对象的各个部件
- getResult(),用于返回复杂对象
-
具体建造者(ConcreteBuilder),它实现了 Builder 接口,实现各个部件的具体构造和装配方法,定义并明确其所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象
-
产品角色(Product),它是被创建的复杂对象,包含多个组成部件
-
指挥者(Director),它负责安排复杂对象的建造次序
- 一方面它隔离了客户与创建过程
- 另一方面它控制产品的创建过程
建造者模式的优点
- 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
- 每个具体建造者都相对独立,而与其它具体建造者无关
- 可以更加精细地控制产品的创建过程
建造者模式的缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似。如果产品之间的差异性很大,就不适合使用创建者模式
- 如果产品的内部结构复杂且多变,可能会需要定义很多具体创建者类来实现这种变化,这就导致系统很庞大,增加系统的理解难度和运行成本
建造者模式的适用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序
- 对象的创建过程独立于创建该对象的类
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品