创建者模式

125 阅读11分钟

1. 创建者模式

创造者模式分为

  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 原型模式
  • 建造者模式

1.1 单例设计模式

单例模式(Singleton Pattern) 是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型设计模式,它提供一种创建对象的最佳方式

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式可以直接访问,不需要实例化该类对象。

1.1.1 单例模式的结构

单例模式的主要有以下角色:

  • 单例类:只能创建一个实例的类
  • 访问类:使用单例类的

4.1.2 单例模式的实现

单例设计模式分类两种:

  • 饿汉式:类加载就会导致该实例对象被创建
  • 懒汉式:类加载不会导致该实例对象被创建,而是首次使用该对象的时候才会创建。
  1. 饿汉式 - 方式一 (静态变量方式)
/**
 * 饿汉式:静态变量方式
 */
public class Singleton {

    // 1. 构造方法私有化
    private Singleton() {
    }

    // 2. 创建一个静态该对象
    private static Singleton instance;

    // 3. 对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}
  1. 饿汉式 - 方式二 (静态代码块)
/**
 * 饿汉式:静态代码块
 */
public class Singleton {

    // 1. 构造方法私有化
    private Singleton() {
    }

    // 2. 声明变量
    private static Singleton instance;

    // 3. 在静态代码块对变量进行赋值
    static {
        instance = new Singleton();
    }

    // 4. 对外提供一个获取该类的方式
    public static Singleton getInstance() {
        return instance;
    }
}
  1. 懒汉式 - 方式一 (线程不安全)
/**
 * 懒汉式
 */
public class Singleton {

    // 1. 私有化构造方法
    private Singleton() {
    }

    // 2. 声明一个该类的对象
    private static Singleton instance;

    // 3. 提供一个对外访问的方式
    public static Singleton getInstance() {
        // 如果不存在一个 Singleton 对象 我就创建这个对象 不然就使用原来的对象
        if (instance == null)
            instance = new Singleton();

        return instance;
    }
}
  1. 懒汉式 - 方式二 (线程安全的)
/**
 * 懒汉式
 */
public class Singleton {

    // 1. 私有化构造方法
    private Singleton() {
    }

    // 2. 声明一个该类的对象
    private static Singleton instance;

    // 3. 提供一个对外访问的方式
    public static synchronized Singleton getInstance() {
        // 如果不存在一个 Singleton 对象 我就创建这个对象 不然就使用原来的对象
        if (instance == null)
            instance = new Singleton();

        return instance;
    }
}

1.2 工厂模式

1.2.1 概述

需求:设计一个咖啡店点餐系统。

设计一个咖啡类(Coffee),并定义其两个子类美式咖啡(AmericanCoffee)和拿铁咖啡(LatteCoffee);在设计一个咖啡店,咖啡店(CoffeeStore)具体有点咖啡的功能。

具体类的设计如下: image.png

在 java 中,万物皆对象,这些对象都需要创建,如果创建的时候直接 new 该对象,就会对该对象耦合严重,假如我们要更换对象,所有 new 对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

1.2.2 简单工厂模式

简单工厂不是一种设计模式,反而比较像一种编程习惯。

1.2.2.1 结构

简单工厂包括如下角色:

  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能
  • 具体产品:实现或者继承抽象产品的子类
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品
1.2.2.2 实现

现在使用简单工厂对上面案例进行改进,类图如下:

image.png

工厂类代码如下:

public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        Coffee coffee;
        if ("american".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("对不起,本店没有您需要点的咖啡");
        }

        return coffee;
    }
}

工厂处理创建对象的细节,一旦有了 SimpleCoffeeFactory,CoffeeStore类中的 orderCoffee() 就变成此对象的客户。后期如果在加新品种的咖啡,我们势必要需求修改 SimpleCoffeeFactory 的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的操作。

1.2.2.3

优点:

封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在源代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

缺点:

增加新产品时还是需要修改工厂类的代码,违背了开闭原则

1.2.3 工厂方法模式

针对上例中的缺点,使用工厂方法模式就可以完美的解决,完全遵循开闭原则。

1.2.3.1 概念

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

1.2.3.2 结构

工厂方法模式的主要角色:

  • 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品:定义了产品的规范,描述了产品的主要特征和功能
  • 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
1.2.3.3 实现

image.png

1.2.3.3 优缺点

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则 缺点:
  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度

1.2.4 抽象工厂模式

前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视厂只生产电视机等

这些工厂只生产同类型产品,同类型产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中需要工厂是综合形工厂,能生产多等级的产品,如电器厂即生产电视机又生产洗衣机或空调。

1.2.4.1 概念

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定索要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多哥等级的产品。

1.2.4.2 结构

抽象工厂模式的主要角色如下:

  • 抽象工厂:提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品
  • 具体工厂:蛀牙实现抽象工厂中的多个抽象方法,完成具体产品的创建
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品
  • 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间多对一的关系。
1.2.4.3 实现

image.png

1.2.4.4 优缺点

优点: 当增加一个产品族的多个对象被设计成一起工作时,他能保证客户始终只使用同一个产品族中的对象 缺点: 当产品族中需要增加一个新的产品时,所有工厂类都要修改

1.3 原型模式

1.3.1 概述

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

1.3.2 结构

原型模式包含如下角色:

  • 抽象原型类:规定了具体原型对象必须实现的 clone() 方法
  • 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象
  • 访问类:使用具体原型类中的 clone() 方法来复制新的对象 接口类图如下:

1.3.3 实现

原型模式的克隆分为浅克隆和深克隆

浅克隆:创建一个新对象,新对象的属性和原来的对象完全相同,对于非基本数据类型,任指向原有属性所指向的对象的内存地址 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址

Java 中的 Object 类中提供了 clone() 方法来实现浅克隆。代码如下:

1.4 建造者模式

1.4.1 概述

将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

  • 分离了部件的构造(由 Builder 来负责)和装配(由 Director 负责)。从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况。
  • 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。这就是实现了构建算法、装配算法的解耦,实现了更好的复用。
  • 建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节

1.4.2 结构

建造者(Builder)模式包含如下角色:

  • 抽象建造者类(Builder): 这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的对象部件的创建。
  • 具体建造者(ConcreteBuilder): 实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
  • 产品类(Product): 要创建的复杂对象。
  • 指挥者类(Director): 调用具体建造者来创建复杂的对象的各个部分,在指导者不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

1.4.3 实例

生产自行车是一个复杂的过程,它包含了车架,车座等组件;Builder 是抽象建造者,MobikeBuilder 和 OfoBuilder 是具体的建造者;Director 是指挥者。

1.4.4 优缺点

优点:

  • 建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑都封装在指挥者类中对整体而言可以取得比较好的稳定性。
  • 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

缺点:

建造者模式所创建的产品一般具有较多的共同点,其组成部分相识,如果产品之间的差异性很大,则不适合建造者模式,因此其使用范围受到一定的限制。

1.5 创建者模式对比

1.5.1 工厂方法模式 VS 建造者模式

工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。

我们举个简单例子,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔的超人;但是使用建造者模式,则需要组装手、脚头等部分。

1.5.2 抽象工厂 VS 建造者模式

抽象工厂模式实现对产品家族的创建,一个产品家族就是这样的一个系列产品:具有不同分类维度的产品组,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。

建造者模式则是要求按照指定的蓝图建造产品,它是主要通过组装零配件而产生一个新产品。