Builder模式:首选哪种变体?

我正在阅读Effective Java书籍,并为我将来的参考创建笔记,我遇到了Builder Pattern。

嗯,我明白它是什么以及如何使用它。在这个过程中,我创建了构建器模式的两个示例变体。

我需要帮助列出每个人的差异和优势吗? 我当然注意到, Example 1暴露了较少的方法,通过较少限制和更通用,允许它更灵活地使用。

请指出我错过的其他事情?

例1

 package item2; /** * @author Sudhakar Duraiswamy * */ public class Vehicle { private String type; private int wheels; interface Builder{ public T build(); } public static class CarBuilder implements Builder{ private String type; private int wheels; CarBuilder createVehicle(){ this.type= "Car"; return this; } CarBuilder addWheels(int wheels){ this.wheels = wheels; return this; } public Vehicle build(){ Vehicle v = new Vehicle(); v.type = type; v.wheels = wheels; return v; } } public static class TruckBuilder implements Builder{ private String type; private int wheels; TruckBuilder createVehicle(){ this.type= "Truck"; return this; } TruckBuilder addWheels(int wheels){ this.wheels = wheels; return this; } public Vehicle build(){ Vehicle v = new Vehicle(); v.type = type; v.wheels = wheels; return v; } } public Vehicle(){ } public static void main(String[] args) { //This builds a car with 4 wheels Vehicle car = new Vehicle.CarBuilder().createVehicle().addWheels(4).build(); //THis builds a Truck with 10 wheels Vehicle truck = new Vehicle.TruckBuilder().createVehicle().addWheels(10).build(); } } 

例2

 package item2; /** * @author Sudhakar Duraiswamy * */ public class Vehicle2 { private String type; private int wheels; interface Builder{ public T build(); public String getType(); public int getWheels() ; } public static class CarBuilder implements Builder{ private String type; private int wheels; public String getType() { return type; } public int getWheels() { return wheels; } CarBuilder createVehicle(){ this.type= "Car"; return this; } CarBuilder addWheels(int wheels){ this.wheels = wheels; return this; } public Vehicle2 build(){ return new Vehicle2(this); } } public static class TruckBuilder implements Builder{ private String type; private int wheels; public String getType() { return type; } public int getWheels() { return wheels; } TruckBuilder createVehicle(){ this.type= "Truck"; return this; } TruckBuilder addWheels(int wheels){ this.wheels = wheels; return this; } public Vehicle2 build(){ return new Vehicle2(this); } } public Vehicle2(Builder builder){ Vehicle2 v = new Vehicle2(); v.type = builder.getType(); v.wheels = builder.getWheels(); } public Vehicle2(){ } public static void main(String[] args) { //This builds a car with 4 wheels Vehicle2 car = new Vehicle2.CarBuilder().createVehicle().addWheels(4).build(); //THis builds a Truck with 10 wheels Vehicle2 truck = new Vehicle2.TruckBuilder().createVehicle().addWheels(10).build(); } } 

以上都不是。

第一个不允许构建不可变的Vehicle,这通常是使用Builder模式的原因。

第二个示例是第一个示例的变体,它允许使用其他getter方法从构建器获取信息。 但是那些方法不会在任何地方使用,除了在Vehicle构造函数中,它可以直接访问构建器字段。 我没有看到添加它们的重点。

我看到有两件更重要的事情需要改进:

  1. 这两种构建器类型完全相同。 不需要两种类型。 一个就足够了。
  2. createVehicle()方法的作用应该由构建器构造函数完成。 如果你构建一个CarBuilder,显然是要建造一辆汽车,所以一旦构建了构建器,就应该设置车辆的类型。 这是我写它的方式:

 public final class Vehicle { private final String type; private final int wheels; private Vehicle(Builder builder) { this.type = builder.type; this.wheels = builder.wheels; } public static Builder carBuilder() { return new Builder("car"); } public static Builder truckBuilder() { return new Builder("truck"); } public static class Builder { private final String type; private int wheels; private Builder(String type) { this.type = type; } public Builder addWheels(int wheels){ this.wheels = wheels; return this; } public Vehicle build() { return new Vehicle(this); } } public static void main(String[] args) { Vehicle car = Vehicle.carBuilder().addWheels(4).build(); Vehicle truck = Vehicle.truckBuilder().addWheels(10).build(); } } 

还有第三个变体,代码较少:

构建者也可以改变Vehicle的状态,而不是拥有自己的实例字段。 内部类可以编写其外部类的私有成员:

 class Vehicle { private int wheels; private Vehicle() {} public static class Builder { private boolean building = true; private Vehicle vehicle = new Vehicle(); public Builder buildWheels(int wheels) { if(!this.building) throw new IllegalStateException(); this.vehicle.wheels = wheels; return this; } public Vehicle build() { this.building = false; return this.vehicle; } } } 

由于这些字段是私有的,你只允许它构建一次building标志),所以构建的Vehicle实例仍然是不可变的消费者,即使这些字段不再是final (不再是realio-trulio不变性 ,请参阅Eric的博客文章 , C#但概念类似)。

您需要更加小心,因为在构造对象期间不必初始化非最终字段(由编译器强制执行),您必须仔细检查building状态。 但是,您可以保存所有实例字段的完整额外副本。 通常,如果您有一组相当大的实例变量(使用相当少的方法构建),这很有用,其中每个方法一次构建几个字段。

我知道这并没有指出您的方法的任何优点或缺点。 但是,如果您不需要final字段,这种方法可以节省大量额外代码。