我正在阅读Effective Java书籍,并为我将来的参考创建笔记, 我遇到了Builder Pattern。
我明白它是什么以及如何使用它。在这个过程中,我创建了两个构建模式的变体。
我需要帮助列出每个人的差异和优势吗?
好吧,我当然注意到,Example 1
暴露的方法较少,而且限制性较小
更通用的,允许它更灵活地使用。
请指出我错过的其他事情?
示例1
package item2;
/**
* @author Sudhakar Duraiswamy
*
*/
public class Vehicle {
private String type;
private int wheels;
interface Builder<T>{
public T build();
}
public static class CarBuilder implements Builder<Vehicle>{
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<Vehicle>{
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<T>{
public T build();
public String getType();
public int getWheels() ;
}
public static class CarBuilder implements Builder<Vehicle2>{
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<Vehicle2>{
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<? extends Vehicle2> 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();
}
}
答案 0 :(得分:8)
以上都不是。
第一个不允许构建不可变的Vehicle,这通常是使用Builder模式的原因。
第二个示例是第一个示例的变体,它允许使用其他getter方法从构建器获取信息。但是那些方法不会在任何地方使用,除了在Vehicle构造函数中,它可以直接访问构建器字段。我没有看到添加它们的重点。
我认为有两件更重要的事情需要改进:
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();
}
}
答案 1 :(得分:2)
还有第三种变体,代码较少:
构建器也可以改变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
(C#上不再有realio-trulio immutability, see Eric's blog article,但概念类似)。
您需要更加小心,因为在构造对象期间不需要初始化非最终字段(由编译器强制执行),您必须仔细检查building
状态。但是,您可以保存所有实例字段的完整额外副本。一般来说,如果你有一组相当大的实例变量,这些变量是用很少的方法构建的,每个方法一次构建几个字段,这很有用。
我知道这并未指出您的方法的任何优点或缺点。但是,如果您不需要将字段设置为final
,则此方法可以节省大量额外代码。