创建型模式的主要关注点是”怎样创建对象”,它是要将”对象的创建与使用进行分离”来降低耦合度,是对象的使用者不需要关注对象的创建过程

创建型模式发分类

1.单例模式: 一个类只能产生一个实例
2.原型模式: 将一个对象作为原型,通过对其进行复制来产生新实例
3.工厂方法: 定义一个用于创建产品的接口,由子类来决定创建什么类型的产品
4.抽象工厂: 定义一个用于创建产品族的接口,有子类来决定创建一系列的产品
5.建造者模式: 将一个复杂的对象分解成多个相当简单的部分,之后根据不同的需求来创建各个部分,最后再将各个部分进行组装

以上五种创建型模式 除了工厂方法模式属于类创建型模式,其他的都属于对象创建型模式

1.单例模式

特点:

1.单例类只产生一个对象
2.这个对象只能由该类创建
3.该单例类对外部提功了一个全局访问接口

优点:

1.因为只提供一个对象,这样会省资源
2.避免了对资源的多重占用
3.单例模式设计了全局访问点, 可以优化对共享资源的访问

缺点:

1.单例模式没有接口, 要修改其功能的话就只能改代码,这样违反了开闭原则
2.在并发测试中,单例模式不利于代码的调试,如果在测试执行的过程中,单例模式的代码没有被执行完, 那样就没法产生对应对象
3.单例模式的功能代码通常封装在一个类中, 如果封装的不合理, 很容易违背单一职责原则

单例模式的应用场景

单例模式可以让jvm中只存在一个单一的实例

  • 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
  • 某类只要求生成一个对象
  • 某类创建的时间消耗较长,且还需要经常创建该类
  • 某对象需要被频繁的创建,同时又频繁的被销毁
  • 频繁访问数据库或文件的对象
  • 对于一些硬件的控制, 或者对于系统来讲,应该是单一的逻辑控制时
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

    单例模式的结构与实现

    通常的类的构造函数是对外public的,可以通过new来创建新对象, 但是对于单例的类来说,它的构造函数是私有的, 只有通过特对外提供的静态方法来提供一个创建对象的方法

单例模式的结构

单例模式的主要形式如下:

  • 单例类:包含一个实例且能自行创建这个实例的类。
  • 访问类: 使用这个实例的类

image.png

单例模式的实现

Singleton 模式通常有两种实现形式。

1.懒汉模式单例

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例

  1. public class LazySingleton {
  2. private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步
  3. private LazySingleton() {
  4. } //private 避免类在外部被实例化
  5. public static synchronized LazySingleton getInstance() {
  6. //getInstance 方法前加同步
  7. if (instance == null) {
  8. instance = new LazySingleton();
  9. }
  10. return instance;
  11. }
  12. }

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

2.饿汉模式单例

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

  1. public class HungrySingleton {
  2. private static final HungrySingleton instance = new HungrySingleton();
  3. private HungrySingleton() {
  4. }
  5. public static HungrySingleton getInstance() {
  6. return instance;
  7. }
  8. }

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

单例模式的扩展

单例模式可扩展为有限的多例(Multitcm)模式,这种模式可生成有限个实例并保存在 ArrayList 中,客户需要时可随机获取,其结构图如图 5 所示。
image.png

2.原型模式

在某些系统中,存在大量相同或相似的对象, 如果用传统的构造函数去创造对象,会比较复杂且消耗资源

定义
用一个已经创建的实例作为原型,通过对原型进行复制,生成相同或相似的新对象,在这里原型实例指定了创建对象的类型, 用这种方式创建对象非常的高效

原型模式的优点:

  • Java自带的原型模式基于二进制流的复制 , 在性能上比new一个对象更优良
  • 可以使用深度克隆的方式保存对象的状态, 使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便需要的时候使用,可辅助实现撤销

    原型模式的缺点:

  • 需要为每个类都配置一个clone方法

  • clone方法位于类的内部, 如果需要修改需要改类的代码, 违反了开闭原则
  • 使用深克隆的时候,需要的逻辑比较复杂,而且对象之间存在多重嵌套,为了实现深度克隆,每一层对象的类都必须支持克隆,实现起来比较麻烦

    原型模式的结构与实现

    因为java提供了对象的clone()方法,所有原型模式实现起来比较简单

    1. 模式的结构

    原型模式主要包括以下角色:
    1.抽象原型类:规定了具体原型对象必须实现的接口
    2.具体原型类:实现抽象原型类的clone方法,它是可被复制的对象
    3.访问类: 使用具体原型类中的clone()方法来复制新对象

其结构图如图 1 所示。

创建型模式的特点及分类 - 图3

2. 模式的实现

  • 浅克隆, 创建一个新的对象,新对象的属性跟原对象的属性完全相同,对于非基本类型的属性, 扔指向原有属性所指向的对象地址
  • 深克隆, 创建一个新对象, 属性中引用的其他对象也会被克隆, 不在指向原属性所指向的地址

Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。其代码如下:

  1. //具体原型类
  2. class Realizetype implements Cloneable {
  3. Realizetype() {
  4. System.out.println("具体原型创建成功!");
  5. }
  6. public Object clone() throws CloneNotSupportedException {
  7. System.out.println("具体原型复制成功!");
  8. return (Realizetype) super.clone();
  9. }
  10. }
  11. //原型模式的测试类
  12. public class PrototypeTest {
  13. public static void main(String[] args) throws CloneNotSupportedException {
  14. Realizetype obj1 = new Realizetype();
  15. Realizetype obj2 = (Realizetype) obj1.clone();
  16. System.out.println("obj1==obj2?" + (obj1 == obj2));
  17. }
  18. }
  19. 结果:
  20. 具体原型创建成功!
  21. 具体原型复制成功!
  22. obj1==obj2?false

原型模式的应用场景

原型模式通常适用于以下场景。

  • 对象之间相同或相似,即只是个别的几个属性不同的时候。
  • 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  • 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  • 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。

    原型模式的扩展

    原型模式可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型,Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型。其结构图如图 5 所示。

创建型模式的特点及分类 - 图4

【例3】用带原型管理器的原型模式来生成包含“圆”和“正方形”等图形的原型,并计算其面积。分析:本实例中由于存在不同的图形类,例如,“圆”和“正方形”,它们计算面积的方法不一样,所以需要用一个原型管理器来管理它们,图 6 所示是其结构图。

创建型模式的特点及分类 - 图5

  1. import java.util.*;
  2. interface Shape extends Cloneable {
  3. public Object clone(); //拷贝
  4. public void countArea(); //计算面积
  5. }
  6. class Circle implements Shape {
  7. public Object clone() {
  8. Circle w = null;
  9. try {
  10. w = (Circle) super.clone();
  11. } catch (CloneNotSupportedException e) {
  12. System.out.println("拷贝圆失败!");
  13. }
  14. return w;
  15. }
  16. public void countArea() {
  17. int r = 0;
  18. System.out.print("这是一个圆,请输入圆的半径:");
  19. Scanner input = new Scanner(System.in);
  20. r = input.nextInt();
  21. System.out.println("该圆的面积=" + 3.1415 * r * r + "\n");
  22. }
  23. }
  24. class Square implements Shape {
  25. public Object clone() {
  26. Square b = null;
  27. try {
  28. b = (Square) super.clone();
  29. } catch (CloneNotSupportedException e) {
  30. System.out.println("拷贝正方形失败!");
  31. }
  32. return b;
  33. }
  34. public void countArea() {
  35. int a = 0;
  36. System.out.print("这是一个正方形,请输入它的边长:");
  37. Scanner input = new Scanner(System.in);
  38. a = input.nextInt();
  39. System.out.println("该正方形的面积=" + a * a + "\n");
  40. }
  41. }
  42. class ProtoTypeManager {
  43. private HashMap<String, Shape> ht = new HashMap<String, Shape>();
  44. public ProtoTypeManager() {
  45. ht.put("Circle", new Circle());
  46. ht.put("Square", new Square());
  47. }
  48. public void addshape(String key, Shape obj) {
  49. ht.put(key, obj);
  50. }
  51. public Shape getShape(String key) {
  52. Shape temp = ht.get(key);
  53. return (Shape) temp.clone();
  54. }
  55. }
  56. public class ProtoTypeShape {
  57. public static void main(String[] args) {
  58. ProtoTypeManager pm = new ProtoTypeManager();
  59. Shape obj1 = (Circle) pm.getShape("Circle");
  60. obj1.countArea();
  61. Shape obj2 = (Shape) pm.getShape("Square");
  62. obj2.countArea();
  63. }
  64. }

3.简单工厂模式

说白了就是 只通过一个类来创建几个(不多)对象,如果通过多个类创建多个对象就是工厂方法
在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。
注意:上述复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况,因为类的构造过于复杂,如果直接在其他业务类内使用,则两者的耦合过重,后续业务更改,就需要在任何引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。

工厂模式的定义:

定义一个创建产品对象的工厂接口,具体创建对象的实现交给其子类,这满足创建型模式中所要求的“创建与使用相分离”的特点。
按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。
我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。
简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在 GoF 23 种设计模式之列。
简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。

优点:

  1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  2. 客户端无需知道所创建具体产品的类名,只需知道参数即可。
  3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

    缺点:

  4. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。

  5. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
  6. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
  7. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

    应用场景

    对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

结构与角色

简单工厂模式的主要角色如下:

  • 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
  • 具体产品(ConcreteProduct):是简单工厂模式的创建目标。

4.工厂方法模式

在《简单工厂模式》一节我们介绍了简单工厂模式,提到了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

    缺点:

  • 类的个数容易过多,增加复杂度

  • 增加了系统的抽象性和理解难度
  • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

    应用场景:

  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。

  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌

    模式的结构与实现

    工厂方法模式的主要角色如下。
  1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
  2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

创建型模式的特点及分类 - 图6

5.抽象工厂模式

本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,图 1 所示的是海尔工厂和 TCL 工厂所生产的电视机与空调对应的关系图。
创建型模式的特点及分类 - 图7

模式的定义与特点

抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
使用抽象工厂模式一般要满足以下条件。

  • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。

  • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  • 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
  • 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

模式的结构与实现

抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。现在我们来分析其基本结构和实现方法。

创建型模式的特点及分类 - 图8

模式的实现

从图 可以看出抽象工厂模式的结构同工厂方法模式的结构相似,不同的是其产品的种类不止一个,所以创建产品的方法也不止一个。下面给出抽象工厂和具体工厂的代码。
(1) 抽象工厂:提供了产品的生成方法。

  1. interface AbstractFactory {
  2. public Product1 newProduct1();
  3. public Product2 newProduct2();
  4. }

(2) 具体工厂:实现了产品的生成方法。

  1. class ConcreteFactory1 implements AbstractFactory {
  2. public Product1 newProduct1() {
  3. System.out.println("具体工厂 1 生成-->具体产品 11...");
  4. return new ConcreteProduct11();
  5. }
  6. public Product2 newProduct2() {
  7. System.out.println("具体工厂 1 生成-->具体产品 21...");
  8. return new ConcreteProduct21();
  9. }
  10. }

模式的应用场景

抽象工厂模式通常适用于以下场景:

  1. 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  2. 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  3. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

抽象工厂模式的扩展有一定的“开闭原则”倾斜性:

  1. 当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
  2. 当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。

    6.建造者模式

    在软件开发过程中有时需要创建一个复杂的对象,这个复杂对象通常由多个子部件按一定的步骤组合而成。例如,计算机是由 CPU、主板、内存、硬盘、显卡、机箱、显示器、键盘、鼠标等部件组装而成的,采购员不可能自己去组装计算机,而是将计算机的配置要求告诉计算机销售公司,计算机销售公司安排技术人员去组装计算机,然后再交给要买计算机的采购员。
    以上所有这些产品都是由多个部件构成的,各个部件可以灵活选择,但其创建步骤都大同小异。这类产品的创建无法用前面介绍的工厂模式描述,只有建造者模式可以很好地描述该类产品的创建。

    模式的定义与特点

    建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。

该模式的主要优点如下:

  1. 封装性好,构建和表示分离。
  2. 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  3. 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

其缺点如下:

  1. 产品的组成部分必须相同,这限制了其使用范围。
  2. 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。

模式的结构与实现

建造者(Builder)模式由产品、抽象建造者、具体建造者、指挥者等 4 个要素构成,现在我们来分析其基本结构和实现方法。

1. 模式的结构

建造者(Builder)模式的主要角色如下。

  1. 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
  2. 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
  3. 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
  4. 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

创建型模式的特点及分类 - 图9
(1) 产品角色:包含多个组成部件的复杂对象。、

  1. class Product {
  2. private String partA;
  3. private String partB;
  4. private String partC;
  5. public void setPartA(String partA) {
  6. this.partA = partA;
  7. }
  8. public void setPartB(String partB) {
  9. this.partB = partB;
  10. }
  11. public void setPartC(String partC) {
  12. this.partC = partC;
  13. }
  14. public void show() {
  15. //显示产品的特性
  16. }
  17. }

(2) 抽象建造者:包含创建产品各个子部件的抽象方法。

  1. abstract class Builder {
  2. //创建产品对象
  3. protected Product product = new Product();
  4. public abstract void buildPartA();
  5. public abstract void buildPartB();
  6. public abstract void buildPartC();
  7. //返回产品对象
  8. public Product getResult() {
  9. return product;
  10. }
  11. }

(3) 具体建造者:实现了抽象建造者接口。

  1. public class ConcreteBuilder extends Builder {
  2. public void buildPartA() {
  3. product.setPartA("建造 PartA");
  4. }
  5. public void buildPartB() {
  6. product.setPartB("建造 PartB");
  7. }
  8. public void buildPartC() {
  9. product.setPartC("建造 PartC");
  10. }
  11. }

(4) 指挥者:调用建造者中的方法完成复杂对象的创建。

  1. class Director {
  2. private Builder builder;
  3. public Director(Builder builder) {
  4. this.builder = builder;
  5. }
  6. //产品构建与组装方法
  7. public Product construct() {
  8. builder.buildPartA();
  9. builder.buildPartB();
  10. builder.buildPartC();
  11. return builder.getResult();
  12. }
  13. }

(5) 客户类。

  1. public class Client {
  2. public static void main(String[] args) {
  3. Builder builder = new ConcreteBuilder();
  4. Director director = new Director(builder);
  5. Product product = director.construct();
  6. product.show();
  7. }
  8. }

模式的应用场景

建造者模式唯一区别于工厂模式的是针对复杂对象的创建。也就是说,如果创建简单对象,通常都是使用工厂模式进行创建,而如果创建复杂对象,就可以考虑使用建造者模式。

建造者模式和工厂模式的区别

通过前面的学习,我们已经了解了建造者模式,那么它和工厂模式有什么区别呢?

  • 建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。
  • 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
  • 关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。
  • 建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。