一篇文章带你了解设计模式——创建者模式

在之前的文章中我们已经学习了设计模式的基本原则和基本分类

下面我们来介绍第一种设计模式,创建型模式的主要关注点是怎样创建对象,它的主要特点是“将对象的创建与使用分离”。

下面我们将从下面四个方面讲述五种创建者模式:

  • 单例模式
  • 工厂模式
  • 原型模式
  • 建造者模式

单例模式

首先我们来介绍单例模式

单例模式介绍

单例模式在我的文章中已经是第三次出现了,所以下面我们做一个简单的介绍:

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

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

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

单例模式展示

单例模式的创建主要分为三种,下面我们来一一介绍:

  1. 饿汉式
/*

饿汉式:类加载就会导致该单实例对象被创建	

*/

/**
 * 饿汉式
 *      静态变量创建类的对象
 */
public class Singleton {
    // 私有构造方法
    private Singleton() {}

    // 在成员位置创建该类的对象并直接初始化
    private static Singleton instance = new Singleton();

    // 对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}

/**
 * 饿汉式
 *      在静态代码块中创建该类对象
 */
public class Singleton {

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

    //在成员位置创建该类的对象
    private static Singleton instance;

    // 直接在静态代码块中进行初始化,一旦类产生就直接创建单例对象,为饿汉式
    static {
        instance = new Singleton();
    }

    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}
  1. 懒汉式
/*

懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建

*/

/**
 * 懒汉式
 *  线程不安全
 */
public class Singleton {
    //私有构造方法
    private Singleton() {}

    //在成员位置创建该类的对象
    private static Singleton instance;

    //对外提供静态方法获取该对象
    public static Singleton getInstance() {

        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

/**
 * 懒汉式
 *  线程安全
 */
public class Singleton {
    //私有构造方法
    private Singleton() {}

    //在成员位置创建该类的对象
    private static Singleton instance;

    //对外提供静态方法获取该对象
    public static synchronized Singleton getInstance() {

        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

/**
 * 懒汉式
 * 双重检查方式
 */
public class Singleton {

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

    private static volatile Singleton instance;

   //对外提供静态方法获取该对象
    public static Singleton getInstance() {
		//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
        if(instance == null) {
            synchronized (Singleton.class) {
                //抢到锁之后再次判断是否为空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

/**
 * 懒汉式
 * 静态内部类方式
 */
public class Singleton {

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

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  1. 恶汉式
/*

恶汉式:采用非类的方法创建单例对象

*/

/**
 * 恶汉式
 * 	枚举方式
 */
public enum Singleton {
    INSTANCE;
}

单例模式应对策略

单例模式通常会出现三种错误,其中两种是可以处理的:

  1. 预防反射破坏单例对象
/*

反射破坏单例对象原理:
	通过反射获得类本身,通过类的构造方法创建新的类对象以破坏单例对象
	
预防反射破坏单例对象原理:
	修改类的构造方法,使构造方式失效或者使构造方法直接返回单例对象而不是创建新的对象

*/

/*代码展示*/

public class Singleton implements Serializable{
    
    // 首先我们需要拥有一个私有的构造方法(为了防止其他对象调用构造方法产生新对象)
    private Singleton(){
        
        // 这里我们需要做一个判断,如果已存在单例对象,且其他对象调用构造方法,直接报错(为了预防反射获得类然后新创对象)
        if( INSTANCE != null){
            throw new RuntimeException("单例对象不可重复创建");
        }
        
        System.out.println("private Singleton");
        
    }
    
    private static final Singleton INSTANCE = new Singleton();
    
    public static Singleton getInstance(){
        return INSTANCE;
    }
    
    public static void otherMethod(){
        System.out.println("otherMethod");
    }
    
}
  1. 预防反序列化破坏单例对象
/*

反序列化破坏单例对象原理:
	调用readResolve方法使用字节流输入输出获得新的对象

预防反序列化破坏单例对象原理:
	重写readResolve方法使其直接返回单例对象

*/

/*代码展示*/

public class Singleton implements Serializable {

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

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    
    /**
     * 下面是为了解决序列化反序列化破解单例模式
     */
    private Object readResolve() {
        return SingletonHolder.INSTANCE;
    }
}

工厂模式

下面我们来介绍工厂模式,主要会介绍到三种工厂模式的使用

工厂模式思考

在正式介绍工厂模式之前,我们首先给出没有工厂模式会出现的问题:

一篇文章带你了解设计模式——创建者模式-LMLPHP

具体问题:

/*

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

具体想法:
	设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);
	再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。
		
问题展示:
	在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。

*/

简单工厂模式

简单工厂模式不是二十三种设计模式成员,他更倾向于是一种思想

简单工厂模式结构

简单工厂包含如下角色:

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

简单工厂模式实战

我们如果使用简单工厂模式去修改前面的问题:

一篇文章带你了解设计模式——创建者模式-LMLPHP

具体分析:

/*

工厂(factory)处理创建对象的细节,一旦有了SimpleCoffeeFactory,CoffeeStore类中的orderCoffee()就变成此对象的客户,后期如果需要Coffee对象直接从工厂中获取即可。这样也就解除了和Coffee实现类的耦合,同时又产生了新的耦合,CoffeeStore对象和SimpleCoffeeFactory工厂对象的耦合,工厂对象和商品对象的耦合。

后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。

*/

/* 工厂类代码 */

public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffee;
    }
}

简单工厂模式分析

我们分别给出简单工厂模式的优缺点:

  • 优点

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

  • 缺点

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

简单工厂模式扩展

我们以简单工厂模式为基准可以创新出静态工厂模式(注:静态工厂模式也不是二十三种设计模式成员):

public class SimpleCoffeeFactory {

    // 将方法设置为静态,主类可以直接使用工厂的方法而省去创建对象的一步
    public static Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffe;
    }
}

工厂方法模式

工厂方法模式属于二十三种设计模式成员

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

工厂方法模式结构

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

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

工厂方法模式实战

我们如果使用工厂方法模式去修改前面的问题:

一篇文章带你了解设计模式——创建者模式-LMLPHP

具体分析:

/*

要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。

工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

*/

/* 咖啡店类 */

public class CoffeeStore {

    private CoffeeFactory factory;

    public CoffeeStore(CoffeeFactory factory) {
        this.factory = factory;
    }

    public Coffee orderCoffee(String type) {
        Coffee coffee = factory.createCoffee();
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    }
}

/* 抽象工厂 */

public interface CoffeeFactory {

    Coffee createCoffee();
}

/* 具体工厂 */

public class LatteCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

public class AmericanCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

工厂方法模式分析

我们分别给出工厂方法模式的优缺点:

  • 优点

用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;

在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;

  • 缺点

每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

抽象工厂模式

抽象工厂模式属于二十三种设计模式成员

抽象工厂模式实际上就是工厂方法模式的升级模式,他将一系列相关的类的创建都产生在同一个工厂中,使其一个工厂类产生多个产品类

抽象工厂模式结构

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

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

抽象工厂模式实战

我们的例题发生了一点点的改变,我们所需要的产品类增多后,单单使用工厂方法模式显得过于繁杂:

一篇文章带你了解设计模式——创建者模式-LMLPHP

具体分析:

/* 

题目更改:
	现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。

*/

/* 抽象工厂 */

public interface DessertFactory {

    Coffee createCoffee();

    Dessert createDessert();
}

/* 具体工厂 */

//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    public Dessert createDessert() {
        return new MatchaMousse();
    }
}
//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    public Dessert createDessert() {
        return new Tiramisu();
    }

抽象工厂模式分析

我们首先给出抽象工厂模式的适用场景:

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。

  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。

  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

  • 如:输入法换皮肤,一整套一起换。生成不同操作系统的程序。

我们再分别给出抽象工厂模式的优缺点:

  • 优点

当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

  • 缺点

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

原型模式

下面我们来介绍原型模式

原型模式介绍

首先我们对原型模式做一个简单的介绍:

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

原型模式结构

原型模式包含如下角色:

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

原型模式关系图如下:

一篇文章带你了解设计模式——创建者模式-LMLPHP

原型模式实现

原型模式的实现主要分为两种实现方式:

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

其主要实现方法来自于:

  • Java中的Object类中提供了 clone() 方法来实现浅克隆。
  • Cloneable 接口是上面的类图中的抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类。

我们给出一个简单的原型模式实现:

/* 抽象原型类:实际上就是Object,已经实现了clone方法 */

/* 具体原型类 */

public class Realizetype implements Cloneable {

    public Realizetype() {
        System.out.println("具体的原型对象创建完成!");
    }

    @Override
    protected Realizetype clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!");
        return (Realizetype) super.clone();
    }
}

/* 测试访问类 */

public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Realizetype r1 = new Realizetype();
        Realizetype r2 = r1.clone();

        System.out.println("对象r1和r2是同一个对象?" + (r1 == r2));
    }
}

原型模式案例

我们首先给出一个简单的案例:

  • 同一学校的“三好学生”奖状除了获奖人姓名不同,其他都相同
  • 可以使用原型模式复制多个“三好学生”奖状出来,然后在修改奖状上的名字即可。

那么我们可以直接使用浅克隆来完成上述案例:

//奖状类
public class Citation implements Cloneable {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return (this.name);
    }

    public void show() {
        System.out.println(name + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

//测试访问类
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation c1 = new Citation();
        c1.setName("张三");

        //复制奖状
        Citation c2 = c1.clone();
        //将奖状的名字修改李四
        c2.setName("李四");

        c1.show();
        c2.show();
    }
}

但是当我们修改了内部属性,将简单对象改为了引用对象时,两者的引用对象都会指向原本的地址,就会导致修改一处造成全部修改

所以我们可以采用深克隆来完成这个操作:

//奖状类
public class Citation implements Cloneable {
    private Student stu;

    public Student getStu() {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }

    void show() {
        System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

//学生类
public class Student {
    private String name;
    private String address;

    public Student(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

// 测试访问类
public class CitationTest1 {
    public static void main(String[] args) throws Exception {
        Citation c1 = new Citation();
        Student stu = new Student("张三", "西安");
        c1.setStu(stu);

        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        //将c1对象写出到文件中
        oos.writeObject(c1);
        oos.close();

        //创建对象出入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        //读取对象
        Citation c2 = (Citation) ois.readObject();
        //获取c2奖状所属学生对象
        Student stu1 = c2.getStu();
        stu1.setName("李四");

        //判断stu对象和stu1对象是否是同一个对象
        System.out.println("stu和stu1是同一个对象?" + (stu == stu1));

        c1.show();
        c2.show();
    }
}

建造者模式

最后我们来介绍建造者模式,它将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式介绍

我们首先来介绍一下建造者模式:

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

建造者模式结构

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

  • 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。

  • 具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。

  • 产品类(Product):要创建的复杂对象。

  • 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

建造者模式关系图如下:

一篇文章带你了解设计模式——创建者模式-LMLPHP

建造者模式实现

我们同样通过一个简单的例子来展示建造者模式:

一篇文章带你了解设计模式——创建者模式-LMLPHP

具体分析:

/*

题目介绍:
	生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。
	而车架又有碳纤维,铝合金等材质的,车座有橡胶,真皮等材质。
	对于自行车的生产就可以使用建造者模式。

关系图介绍:
	这里Director是指挥者;
	Bike是产品,包含车架,车座等组件;
	Builder是抽象建造者,MobikeBuilder和OfoBuilder是具体的建造者;

*/

/* 具体代码展示 */

//自行车类
public class Bike {
    private String frame;
    private String seat;

    public String getFrame() {
        return frame;
    }

    public void setFrame(String frame) {
        this.frame = frame;
    }

    public String getSeat() {
        return seat;
    }

    public void setSeat(String seat) {
        this.seat = seat;
    }
}

// 抽象 builder 类
public abstract class Builder {

    protected Bike mBike = new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();
    public abstract Bike createBike();
}

//摩拜单车Builder类
public class MobikeBuilder extends Builder {

    @Override
    public void buildFrame() {
        mBike.setFrame("铝合金车架");
    }

    @Override
    public void buildSeat() {
        mBike.setSeat("真皮车座");
    }

    @Override
    public Bike createBike() {
        return mBike;
    }
}

//ofo单车Builder类
public class OfoBuilder extends Builder {

    @Override
    public void buildFrame() {
        mBike.setFrame("碳纤维车架");
    }

    @Override
    public void buildSeat() {
        mBike.setSeat("橡胶车座");
    }

    @Override
    public Bike createBike() {
        return mBike;
    }
}

//指挥者类
public class Director {
    private Builder mBuilder;

    public Director(Builder builder) {
        mBuilder = builder;
    }

    public Bike construct() {
        mBuilder.buildFrame();
        mBuilder.buildSeat();
        return mBuilder.createBike();
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        showBike(new OfoBuilder());
        showBike(new MobikeBuilder());
    }
    private static void showBike(Builder builder) {
        Director director = new Director(builder);
        Bike bike = director.construct();
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }
}

/* 部分代码优化:有些情况下需要简化系统结构,可以把指挥者类和抽象建造者进行结合 */

// 抽象 builder 类
public abstract class Builder {

    protected Bike mBike = new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();
    public abstract Bike createBike();
    
    public Bike construct() {
        this.buildFrame();
        this.BuildSeat();
        return this.createBike();
    }
}

建造者模式分析

首先我们给出建造者模式的主要适用场景:

  • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
  • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。

然后我们给出建造者模式的优点:

  • 建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在指挥者类中对整体而言可以取得比较好的稳定性。
  • 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。符合开闭原则。

最后我们给出建造者模式的缺点:

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

建造者模式扩展

建造者模式的另一个营业场景也包括:

  • 当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误
  • 此时就可以利用建造者模式进行重构。

我们给出一个简单例子:

/* 原版代码 */

public class Phone {
    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    public Phone(String cpu, String screen, String memory, String mainboard) {
        this.cpu = cpu;
        this.screen = screen;
        this.memory = memory;
        this.mainboard = mainboard;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getMemory() {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getMainboard() {
        return mainboard;
    }

    public void setMainboard(String mainboard) {
        this.mainboard = mainboard;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

public class Client {
    public static void main(String[] args) {
        //构建Phone对象
        Phone phone = new Phone("intel","三星屏幕","金士顿","华硕");
        System.out.println(phone);
    }
}

/* 建造者模式代码 */

public class Phone {

    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    private Phone(Builder builder) {
        cpu = builder.cpu;
        screen = builder.screen;
        memory = builder.memory;
        mainboard = builder.mainboard;
    }

    public static final class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;

        public Builder() {}

        public Builder cpu(String val) {
            cpu = val;
            return this;
        }
        public Builder screen(String val) {
            screen = val;
            return this;
        }
        public Builder memory(String val) {
            memory = val;
            return this;
        }
        public Builder mainboard(String val) {
            mainboard = val;
            return this;
        }
        public Phone build() {
            return new Phone(this);}
    }
    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone.Builder()
                .cpu("intel")
                .mainboard("华硕")
                .memory("金士顿")
                .screen("三星")
                .build();
        System.out.println(phone);
    }
}

结束语

关于创建者模式我们就介绍到这里,后面我会继续更新二十三种设计模式,希望能给你带来帮助~

附录

该文章属于学习内容,具体参考B站黑马程序员的Java设计模式详解

这里附上视频链接:22.设计模式-创建型模式-单例设计模式概述_哔哩哔哩_bilibili

02-08 00:39