第五天:原型模式、建造者模式

1. 原型模式

1.1 概述

原型模式是一种创建型设计模式,通过复制已有的实例来创建新对象,而不是通过实例化类来创建。这种模式适用于创建复杂对象或者频繁创建和销毁对象的场景。原型模式通过定义一个接口来声明复制方法,具体的实现类需要实现这个方法。

1.2 结构

原型模式包含如下角色:

  • 原型接口(Prototype):定义一个接口,声明克隆方法。
  • 具体原型类(ConcretePrototype):实现原型接口,提供具体的克隆功能。
  • 客户端(Client):使用原型来创建对象,而无需关心对象的具体类。

接口类图如下:

image.png

1.3 实现

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

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

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

// 具体原型类
public class ConcretePrototype implements Cloneable {
    private String field;

    public ConcretePrototype(String field) {
        System.out.println("具体原型对象创建完成!");
        this.field = field;
    }

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    @Override
    public ConcretePrototype clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!");
        return (ConcretePrototype) super.clone();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 输出:具体的原型对象创建完成!
        ConcretePrototype original = new ConcretePrototype("Hello");
        // 输出:具体原型复制成功!
        ConcretePrototype clone = original.clone();
        System.out.println("===修改前===");
        // 输出:原始数据:Hello
        System.out.println("原始数据:" + original.getField());
        // 输出:克隆数据:Hello
        System.out.println("克隆数据:" + clone.getField());
        System.out.println(original == clone);
        // 修改克隆对象的字段
        clone.setField("World");
        System.out.println("===修改后===");
        // 输出:原始数据:Hello
        System.out.println("原始数据:" + original.getField());
        // 输出:克隆数据:World
        System.out.println("克隆数据:" + clone.getField());
        // 判断获取到的两个实例是否是同一个对象, 输出:false
        System.out.println(original == clone);
    }
}

1.3.1 案例

用原型模式生成“三好学生”奖状

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

1.3.1.1 浅克隆

浅克隆是指在克隆对象时,它会创建一个新的对象,新对象的非基本类型(引用类型)属性将与原对象的相应属性指向相同的内存地址。也就是说,浅克隆只是简单地复制对象的基本类型属性的值,而对于对象中的引用类型属性,只是复制了引用,而不是复制引用所指向的对象本身。

// 学生类
public class Student {
    // 学生的姓名
    private String name;
    // 学生的地址
    private String address;

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

    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 Citation implements Cloneable {
    private Student student;

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public void show() {
        System.out.println(student.getName() + "同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!");
        System.out.println("奖状将会发往:" + student.getAddress() + "市");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation citation1 = new Citation();
        Student student1 = new Student("张三", "西安");
        citation1.setStudent(student1);
        // 复制奖状
        Citation citation2 = citation1.clone();
        // 获取citation2奖状所属学生对象
        Student student2 = citation2.getStudent();
        student2.setName("李四");
        student2.setAddress("上海");
        // 判断student1对象和student2对象是否是同一个对象
        System.out.println("student1和student2是同一个对象?" + (student1 == student2));
        // 输出:
        // 李四同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!
        // 奖状将会发往:上海市
        citation1.show();
        // 输出:
        // 李四同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!
        // 奖状将会发往:上海市
        citation2.show();
    }
}

student1student2 是同一个对象,就会产生将 student1 对象中 name 属性值改为“李四”,address 属性值改为“上海”,两个 citation 对象中显示的都是李四。这就是浅克隆的效果,对 具体原型类(Citation) 中的引用类型的属性进行引用的复制。这种情况需要使用深克隆,而进行深克隆需要使用对象流。

1.3.1.2 深克隆

深克隆(Deep Clone)是指在克隆对象时,不仅要复制对象本身的值,还要复制对象内部引用的所有对象,确保克隆出的对象与原对象完全独立。与浅克隆相比,深克隆能避免引用类型数据的共享。

1.3.1.2.1 深克隆的两种常见方法
  1. 实现 Cloneable 接口并进行手动深拷贝
  2. 通过序列化机制实现深拷贝
1.3.1.2.2 手动深拷贝

在这种方法中,所有可变的引用类型属性都需要调用其 clone() 方法来进行克隆。这种方法的优势是灵活性,但可能需要手动处理对象的各个层级,尤其是多级引用时实现起来会较为复杂。

// 学生类
public class Student implements Cloneable {
    // 学生的姓名
    private String name;
    // 学生的地址
    private String address;

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

    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;
    }

    @Override
    public Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }
}
// 奖状类
public class Citation implements Cloneable {
    private Student student;

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public void show() {
        System.out.println(student.getName() + "同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!");
        System.out.println("奖状将会发往:" + student.getAddress() + "市");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        Citation clonedCitation = (Citation) super.clone();
        // 手动克隆引用类型
        clonedCitation.setStudent(clonedCitation.getStudent().clone());
        return clonedCitation;
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation citation1 = new Citation();
        Student student1 = new Student("张三", "西安");
        citation1.setStudent(student1);
        // 复制奖状
        Citation citation2 = citation1.clone();
        // 获取citation2奖状所属学生对象
        Student student2 = citation2.getStudent();
        student2.setName("李四");
        student2.setAddress("上海");
        // 判断student1对象和student2对象是否是同一个对象
        System.out.println("student1和student2是同一个对象?" + (student1 == student2));
        // 输出:
        // 张三同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!
        // 奖状将会发往:西安市
        citation1.show();
        // 输出:
        // 李四同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!
        // 奖状将会发往:上海市
        citation2.show();
    }
}
1.3.1.2.3 手动深拷贝优缺点
  1. 优点:
  • 灵活:可以精确控制每个引用属性的拷贝方式。
  1. 缺点:
  • 实现复杂:每个引用类型属性都需要手动克隆,特别是嵌套对象时,代码复杂度会增加。
1.3.1.2.4 序列化方式深拷贝

这种方法借助 Java 的序列化机制,将对象序列化为字节流,再将其反序列化为一个新的对象,从而实现深拷贝。该方法简单且通用,适合于深拷贝复杂对象。

// 学生类
public class Student implements Serializable {
    // 学生的姓名
    private String name;
    // 学生的地址
    private String address;

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

    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 Citation implements Serializable {
    private Student student;

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public void show() {
        System.out.println(student.getName() + "同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!");
        System.out.println("奖状将会发往:" + student.getAddress() + "市");
    }

    public Citation deepClone() throws IOException, ClassNotFoundException {
        // 写入字节流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        // 读取字节流
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Citation) ois.readObject();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Citation citation1 = new Citation();
        Student student1 = new Student("张三", "西安");
        citation1.setStudent(student1);
        // 复制奖状
        Citation citation2 = citation1.deepClone();
        // 获取citation2奖状所属学生对象
        Student student2 = citation2.getStudent();
        student2.setName("李四");
        student2.setAddress("上海");
        // 判断student1对象和student2对象是否是同一个对象
        System.out.println("student1和student2是同一个对象?" + (student1 == student2));
        // 输出:
        // 张三同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!
        // 奖状将会发往:西安市
        citation1.show();
        // 输出:
        // 李四同学:在2024学年第一学期中表现优秀,被评为三好学生。特发此状!
        // 奖状将会发往:上海市
        citation2.show();
    }
}

注意:Citation 类和 Student 类必须实现 Serializable 接口,否则会抛 NotSerializableException 异常。

1.3.1.2.5 序列化方式深拷贝优缺点
  1. 优点:
  • 简单:不需要手动编写克隆逻辑,序列化和反序列化会自动处理整个对象图的拷贝。
  • 通用性:适用于对象图中包含多层级引用关系的复杂对象。
  1. 缺点:
  • 需要类实现 Serializable 接口。
  • 性能较低:序列化和反序列化相对较慢,可能对性能有影响,特别是大对象时。

1.4 原型模式优缺点

  1. 优点
  • 性能优化:通过克隆已经存在的对象,可以避免重复初始化和复杂构造过程,从而提高性能。
  • 动态性:原型模式可以在运行时决定需要克隆的具体对象,支持灵活的对象创建。
  • 简化对象创建:对于复杂对象,使用原型克隆相较于构造器更加简单直观,尤其是当对象的构造需要很多步骤时。
  1. 缺点
  • 实现复杂性:需要实现 clone 方法,对于一些复杂对象,可能涉及深拷贝和浅拷贝的实现,使得代码复杂。
  • 依赖问题:如果原型对象内部含有复杂的依赖关系或者不可变对象,克隆时可能会导致状态不一致的问题。
  • 限制:在 Java 中,如果原型类没有实现 Cloneable 接口,调用 clone() 方法会抛出异常。需要额外的处理。

1.5 总结

原型模式是一种有效的对象创建方式,尤其适用于对象构造复杂或者频繁创建和销毁的情况。通过克隆已有的实例,可以简化对象创建过程,提高性能,但在实现时需要关注克隆的复杂性和对象状态的一致性问题。

2. 建造者模式

2.1 概述

建造者模式(Builder Pattern)是一种创建型设计模式,用于将复杂对象的创建过程与其表示分离。它允许逐步构建对象,并根据需要动态选择其组成部分,最终通过一个建造者将对象“组装”起来。建造者模式的核心在于将一个复杂对象的构建与它的表示分离开,这样同样的构建过程可以创建不同的表示。

2.2 结构

  • 产品(Product):表示要构建的复杂对象。
  • 抽象建造者(Builder):定义构建对象的步骤,通常包括设置部件的抽象方法。
  • 具体建造者(ConcreteBuilder):实现 Builder 接口,完成实际对象的创建步骤,并返回最终的复杂对象。
  • 指挥者(Director):控制构建过程的逻辑,将建造的步骤按特定顺序执行。
  • 客户端(Client):使用 DirectorBuilder 来生成产品。

接口类图如下:

image.png

2.3 实现

// 汉堡类,表示要构建的产品
public class Burger {
    // 面包
    private String bread;
    // 肉饼
    private String patty;
    // 沙拉
    private String salad;
    // 酱料
    private String sauce;

    public void setBread(String bread) {
        this.bread = bread;
    }

    public void setPatty(String patty) {
        this.patty = patty;
    }

    public void setSalad(String salad) {
        this.salad = salad;
    }

    public void setSauce(String sauce) {
        this.sauce = sauce;
    }

    @Override
    public String toString() {
        return "汉堡里面有:" + bread + "," + patty + "," + salad + "和" + sauce;
    }
}
// 抽象的建造者,定义了构建汉堡的步骤
public interface BurgerBuilder {
    void buildBread();

    void buildPatty();

    void buildSalad();

    void buildSauce();
    
    // 返回构建好的汉堡
    Burger getBurger();
}
// 具体建造者,构建一款经典汉堡
public class ClassicBurgerBuilder implements BurgerBuilder {
    private final Burger burger;

    public ClassicBurgerBuilder() {
        this.burger = new Burger();
    }

    @Override
    public void buildBread() {
        burger.setBread("白面包");
        System.out.println("使用白面包...");
    }

    @Override
    public void buildPatty() {
        burger.setPatty("牛肉饼");
        System.out.println("添加牛肉饼...");
    }

    @Override
    public void buildSalad() {
        burger.setSalad("生菜");
        System.out.println("添加生菜...");
    }

    @Override
    public void buildSauce() {
        burger.setSauce("番茄酱");
        System.out.println("添加番茄酱...");
    }

    @Override
    public Burger getBurger() {
        return this.burger;
    }
}
// 具体建造者,构建一款素食汉堡
public class VeggieBurgerBuilder implements BurgerBuilder {
    private final Burger burger;

    public VeggieBurgerBuilder() {
        this.burger = new Burger();  // 初始化汉堡
    }

    @Override
    public void buildBread() {
        burger.setBread("全麦面包");
        System.out.println("使用全麦面包...");
    }

    @Override
    public void buildPatty() {
        burger.setPatty("豆腐饼");
        System.out.println("添加豆腐饼...");
    }

    @Override
    public void buildSalad() {
        burger.setSalad("黄瓜");
        System.out.println("添加黄瓜...");
    }

    @Override
    public void buildSauce() {
        burger.setSauce("芥末酱");
        System.out.println("添加芥末酱...");
    }

    @Override
    public Burger getBurger() {
        return this.burger;
    }
}
// 指挥者,负责控制构建的过程
public class BurgerDirector {
    private final BurgerBuilder burgerBuilder;

    public BurgerDirector(BurgerBuilder burgerBuilder) {
        this.burgerBuilder = burgerBuilder;
    }
    // 控制建造过程
    public Burger makeBurger() {
        burgerBuilder.buildBread();
        burgerBuilder.buildPatty();
        burgerBuilder.buildSalad();
        burgerBuilder.buildSauce();
        // 返回构建好的汉堡
        return burgerBuilder.getBurger();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建具体的建造者
        BurgerBuilder builder1 = new ClassicBurgerBuilder();
        BurgerBuilder builder2 = new VeggieBurgerBuilder();
        // 创建指挥者并传入建造者
        BurgerDirector director1 = new BurgerDirector(builder1);
        BurgerDirector director2 = new BurgerDirector(builder2);
        // ===开始构建经典汉堡===
        // 使用白面包...
        // 添加牛肉饼...
        // 添加生菜...
        // 添加番茄酱...
        System.out.println("===开始构建经典汉堡===");
        Burger burger1 = director1.makeBurger();
        // ===开始构建素食汉堡===
        // 使用全麦面包...
        // 添加豆腐饼...
        // 添加黄瓜...
        // 添加芥末酱...
        System.out.println("===开始构建素食汉堡===");
        Burger burger2 = director2.makeBurger();
        System.out.println("======");
        // 输出:构建好的经典汉堡: 汉堡里面有:白面包,牛肉饼,生菜和番茄酱
        System.out.println("构建好的经典汉堡: " + burger1);
        // 输出:构建好的素食汉堡: 汉堡里面有:全麦面包,豆腐饼,黄瓜和芥末酱
        System.out.println("构建好的素食汉堡: " + burger2);
    }
}

注意:
上面示例是 Builder 模式的常规用法,指挥者类 Director 在建造者模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把指挥者类和抽象建造者进行结合。

// 抽象的建造者和指挥者,定义了构建汉堡的步骤并且负责控制构建的过程
public abstract class BurgerBuilder {
    protected Burger burger = new Burger();

    abstract void buildBread();

    abstract void buildPatty();

    abstract void buildSalad();

    abstract void buildSauce();

    abstract Burger getBurger();

    // 控制建造过程
    public Burger makeBurger() {
        this.buildBread();
        this.buildPatty();
        this.buildSalad();
        this.buildSauce();
        // 返回构建好的汉堡
        return this.getBurger();
    }
}
// 具体建造者,构建一款经典汉堡
public class ClassicBurgerBuilder extends BurgerBuilder {
    private final Burger burger;

    public ClassicBurgerBuilder() {
        this.burger = new Burger();
    }

    @Override
    public void buildBread() {
        burger.setBread("白面包");
        System.out.println("使用白面包...");
    }

    @Override
    public void buildPatty() {
        burger.setPatty("牛肉饼");
        System.out.println("添加牛肉饼...");
    }

    @Override
    public void buildSalad() {
        burger.setSalad("生菜");
        System.out.println("添加生菜...");
    }

    @Override
    public void buildSauce() {
        burger.setSauce("番茄酱");
        System.out.println("添加番茄酱...");
    }

    @Override
    public Burger getBurger() {
        return this.burger;
    }
}
// 具体建造者,构建一款素食汉堡
public class VeggieBurgerBuilder extends BurgerBuilder {
    private final Burger burger;

    public VeggieBurgerBuilder() {
        this.burger = new Burger();  // 初始化汉堡
    }

    @Override
    public void buildBread() {
        burger.setBread("全麦面包");
        System.out.println("使用全麦面包...");
    }

    @Override
    public void buildPatty() {
        burger.setPatty("豆腐饼");
        System.out.println("添加豆腐饼...");
    }

    @Override
    public void buildSalad() {
        burger.setSalad("黄瓜");
        System.out.println("添加黄瓜...");
    }

    @Override
    public void buildSauce() {
        burger.setSauce("芥末酱");
        System.out.println("添加芥末酱...");
    }

    @Override
    public Burger getBurger() {
        return this.burger;
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建具体的建造者
        BurgerBuilder builder1 = new ClassicBurgerBuilder();
        BurgerBuilder builder2 = new VeggieBurgerBuilder();
        // ===开始构建经典汉堡===
        // 使用白面包...
        // 添加牛肉饼...
        // 添加生菜...
        // 添加番茄酱...
        System.out.println("===开始构建经典汉堡===");
        Burger burger1 = builder1.makeBurger();
        // ===开始构建素食汉堡===
        // 使用全麦面包...
        // 添加豆腐饼...
        // 添加黄瓜...
        // 添加芥末酱...
        System.out.println("===开始构建素食汉堡===");
        Burger burger2 = builder2.makeBurger();
        System.out.println("======");
        // 输出:构建好的经典汉堡: 汉堡里面有:白面包,牛肉饼,生菜和番茄酱
        System.out.println("构建好的经典汉堡: " + burger1);
        // 输出:构建好的素食汉堡: 汉堡里面有:全麦面包,豆腐饼,黄瓜和芥末酱
        System.out.println("构建好的素食汉堡: " + burger2);
    }
}

这样做确实简化了系统结构,但同时也加重了抽象建造者类的职责,也不是太符合单一职责原则,如果建造过程过于复杂,建议还是封装到 Director 中。

2.4 建造者模式优缺点

  1. 优点
  • 更好的控制对象创建过程:通过分步骤创建对象,每一步可以自由选择不同的细节,最终得到精确定制的产品。
  • 代码更简洁、更灵活:客户端无需关心复杂对象的创建过程,只需通过指挥者调用建造者来构建对象。
  • 解耦创建和表示:在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 扩展性好:通过实现不同的 ConcreteBuilder,可以灵活创建多种表示的产品,而无需修改现有的指挥者和产品类。因此也就不会对原有功能引入风险。符合开闭原则。
  1. 缺点
  • 产品内部构建过程较复杂时可能增加复杂性:虽然建造者模式解耦了对象的构建,但如果产品本身过于复杂,可能需要很多 Builder 子类来完成具体的构建任务,反而增加系统的复杂度。
  • 需要有稳定的构建过程:如果产品的构建过程频繁变化,可能需要频繁修改 Builder 接口及其实现类。
  • 需要有共同点:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

2.5 使用场景

  • 需要分步骤创建复杂对象:对象由多个部分组成,并且对象的创建步骤依赖于某些条件或者顺序。
  • 不同表示的对象使用同样的构建过程:同样的创建步骤可以构建出不同的对象表示。
  • 需要将对象的创建过程与表示分离:在需要解耦对象的构建与它的具体表示时,使用建造者模式可以减少耦合。

2.6 模式扩展

建造者模式除了上面的用途外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构。

重构前代码如下:

// 电脑类
public class Computer {
    // CPU
    private String CPU;
    // 屏幕
    private String screen;
    // 内存条
    private String memory;
    // 主板
    private String mainboard;

    public Computer(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 "Computer{" +
                "CPU='" + CPU + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 构建电脑对象
        Computer computer = new Computer("intel", "三星屏幕", "金士顿", "华硕");
        // 输出:Computer{CPU='intel', screen='三星屏幕', memory='金士顿', mainboard='华硕'}
        System.out.println(computer);
    }
}

上面在客户端代码中构建 Computer 对象,传递了四个参数,如果参数更多呢?代码的可读性及使用的成本就是比较高。

重构后代码:

// 电脑类
public class Computer {
    // CPU
    private final String CPU;
    // 屏幕
    private final String screen;
    // 内存条
    private final String memory;
    // 主板
    private final String mainboard;

    private Computer(Builder builder) {
        this.CPU = builder.CPU;
        this.screen = builder.screen;
        this.memory = builder.memory;
        this.mainboard = builder.mainboard;
    }

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

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

        public Builder() {
        }

        public Builder CPU(String CPU) {
            this.CPU = CPU;
            return this;
        }

        public Builder screen(String screen) {
            this.screen = screen;
            return this;
        }

        public Builder memory(String memory) {
            this.memory = memory;
            return this;
        }

        public Builder mainboard(String mainboard) {
            this.mainboard = mainboard;
            return this;
        }
        public Computer build(){
            return new Computer(this);
        }
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 构建电脑对象
        Computer computer1 = new Computer.Builder()
                .CPU("intel")
                .mainboard("华硕")
                .memory("金士顿")
                .screen("三星")
                .build();
        Computer computer2 = new Computer.Builder()
                .CPU("AMD")
                .mainboard("微星")
                .screen("LG")
                .memory("芝奇")
                .build();
        // 输出:Computer{CPU='intel', screen='三星屏幕', memory='金士顿', mainboard='华硕'}
        System.out.println(computer1);
        // 输出:Computer{CPU='AMD', screen='LG', memory='芝奇', mainboard='微星'}
        System.out.println(computer2);
    }
}

重构后的代码在使用起来更方便,某种程度上也可以提高开发效率。从软件设计上,对程序员的要求比较高。

2.7 总结

建造者模式通过将复杂对象的构建过程与它的最终表示解耦,为对象创建提供了灵活性和可扩展性。它可以灵活地构建多种不同的对象,适用于对象需要分步骤创建并且具有多个不同表示的场景。但当对象的构建过程非常复杂或频繁变化时,建造者模式的实现可能会变得冗长和复杂。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容