生成器模式 [英] Builder pattern

查看:151
本文介绍了生成器模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要实现没有静态嵌套类的Builder模式。如果我有继承,最好的做法是什么?
我们假设我有以下课程。

  public class Car {
private String brand;
私人字符串速度;
// getter a setter
}

public class PassengerCar extends Car {
private String capacity;
// getter an setter
}

public class Truck extends Car {
private String length;
// getter a setters
}

最好创建一个Builder将负责设置PassengerCar和Truck的值的课程,或者我们需要3个额外的课程,CarBuilder,PassengerCarBuilder扩展CarBuilder和TruckBuilder扩展CarBuilder?

解决方案

正确的做法是让每个类别一个构建器。我看到两个不同的构建器实现,我们称之为懒惰 eager (也许其中一个不是一个严格的构建器,但是它们实际上都是<



以下是的惰性建设者,汽车卡车 s:

  public abstract class AbstractLazyCarBuilder< T extends Car,B extends AbstractLazyCarBuilder< T,B> {

私人品牌;

私人字符串速度;

public B品牌(String品牌){
this.brand =品牌;
return(B)this;
}

public B speed(String speed){
this.speed = speed;
return(B)this;
}

public T build(){
T car = this.create();
this.fill(car);
回车;
}

protected abstract T create();

protected void fill(T car){
car.setBrand(this.brand);
car.setSpeed(this.speed);
}
}

public class LazyCarBuilder extends AbstractLazyCarBuilder< Car,LazyCarBuilder> {

@Override
protected Car create(){
return new Car();
}
}

public class LazyTruckBuilder extends AbstractLazyCarBuilder< Truck,LazyTruckBuilder> {

private String length;

public LazyTruckBuilder length(String length){
this.length = length;
返回这个;
}

@Override
保护卡车创建(){
返回新卡车();
}

@Override
protected void fill(卡车){
super.fill(truck); // 很重要!用汽车的共同属性填充卡车
truck.setLength(this.length);
}
}

用法:

 卡车卡车=新的LazyTruckBuilder()。品牌(ford)速度(40)长度(30)build() 

它可能不是标准的构建器实现。它有两个通用参数:正在构建的对象的类型和构建器本身的类型。最后一个是在使用构建器方法时避免转换返回的对象。



它有一个 create()方法返回空的特定实例(也可以通过反射创建)以及将所有属性设置为创建对象的 fill()方法。在构建器上调用 build()时,该构建器是懒惰的。对象被创建并初始化。



这个构建器的渴望版本应该使用反射来创建正在构建的对象:

  public abstract class AbstractEagerCarBuilder< T extends Car B扩展了AbstractEagerCarBuilder< T,B>> {

保护final T实例; //需要被子类看到

protected AbstractEagerCarBuilder(){
try {
//反射魔法获取特定车型的类型
ParameterizedType type =(ParameterizedType )this.getClass()。getGenericSuperclass();
Class< T> clazz =(Class< T>)type.getActualTypeArguments()[0];
//通过反射创建特定的车辆
this.instance = clazz.getConstructor()。newInstance();
} catch(Exception e){
throw new RuntimeException(无法创建特定实例,e);
}
}

public B品牌(String品牌){
this.instance.setBrand(brand);
return(B)this;
}

public B speed(String speed){
this.instance.setSpeed(speed);
return(B)this;
}

public T build(){
return this.instance;
}
}

public class EagerCarBuilder extends AbstractEagerCarBuilder< Car,EagerCarBuilder> {
// empty:just pass generic parameters
}

public class EagerTruckBuilder extends AbstractEagerCarBuilder< Truck,EagerTruckBuilder> {

private String length;

public EagerTruckBuilder length(String length){
this.instance.setLength = length;
返回这个;
}
}

用法:

 卡车卡车=新的EagerTruckBuilder()。品牌(gmc)速度(45)长度(32)。 

这里,卡车实例实际上是在建筑师创作时创建的。然后,构建器方法逐个填充热门创建的实例,其属性一个接一个。



是否使用一个或另一个,取决于您。请告诉我,如果这有错误,因为我无法测试它,以及如果你有任何问题(我很习惯这种代码,我可能没有一些有用的解释)。


I need to implement Builder pattern without static nested classes. What is the best approach for doing it if I have inheritance? Let's imagine I have the following classes.

public class Car {
   private String brand;
   private String speed; 
   //getters an setters  
}

public class PassengerCar extends Car{
   private String capacity; 
   //getters an setters  
}

public class Truck extends Car{
   private String length; 
   //getters an setters  
}

Is it better to create one Builder class that will responsible for setting values of PassengerCar and Truck or we need 3 additional classes, CarBuilder, PassengerCarBuilder extends CarBuilder and TruckBuilder extends CarBuilder?

解决方案

The correct approach would be to have one builder per class. I have seen two different builder implementations, let's call them lazy and eager (maybe one of them is not a strict builder, but both of them actually build instances).

Here are lazy builders for Cars and Trucks:

public abstract class AbstractLazyCarBuilder<T extends Car, B extends AbstractLazyCarBuilder<T, B>> {

    private String brand;

    private String speed;

    public B brand(String brand) {
        this.brand = brand;
        return (B) this;
    }

    public B speed(String speed) {
        this.speed = speed;
        return (B) this;
    }

    public T build() {
        T car = this.create();
        this.fill(car);
        return car;
    }

    protected abstract T create();

    protected void fill(T car) {
        car.setBrand(this.brand);
        car.setSpeed(this.speed);
    }
}

public class LazyCarBuilder extends AbstractLazyCarBuilder<Car, LazyCarBuilder> {

    @Override
    protected Car create() {
        return new Car();
    }
}

public class LazyTruckBuilder extends AbstractLazyCarBuilder<Truck, LazyTruckBuilder> {

    private String length;

    public LazyTruckBuilder length(String length) {
        this.length = length;
        return this;
    }

    @Override
    protected Truck create() {
        return new Truck();
    }

    @Override
    protected void fill(Truck truck) {
        super.fill(truck); // very important! fills truck with car's common attributes 
        truck.setLength(this.length);
    }
}

Usage:

Truck truck = new LazyTruckBuilder().brand("ford").speed("40").length("30").build();

It might not be the standard builder implementation. It has two generic parameters: the type of the object being built and the type of the builder itself. This last one is to avoid casting the returned object when using the builder methods.

It has a create() method that returns the empty specific instance (could have also been created by reflection) and a fill() method that sets all the attributes to the created object. This builder is lazy in the sense that the object is created and initialized when you call build() on the builder.

The eager version of this builder should use reflection to create the object being built:

public abstract class AbstractEagerCarBuilder<T extends Car, B extends AbstractEagerCarBuilder<T, B>> {

    protected final T instance; // needs to be seen by subclasses

    protected AbstractEagerCarBuilder() {
        try {
            // Reflection magic to get type of specific car
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            Class<T> clazz = (Class<T>) type.getActualTypeArguments()[0];
            // Create the specific car by reflection
            this.instance = clazz.getConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Could not create specific instance", e);
        }
    }

    public B brand(String brand) {
        this.instance.setBrand(brand);
        return (B) this;
    }

    public B speed(String speed) {
        this.instance.setSpeed(speed);
        return (B) this;
    }

    public T build() {
        return this.instance;
    }
}

public class EagerCarBuilder extends AbstractEagerCarBuilder<Car, EagerCarBuilder> {
    // empty: just pass generic parameters
}

public class EagerTruckBuilder extends AbstractEagerCarBuilder<Truck, EagerTruckBuilder> {

    private String length;

    public EagerTruckBuilder length(String length) {
        this.instance.setLength = length;
        return this;
    }
}

Usage:

Truck truck = new EagerTruckBuilder().brand("gmc").speed("45").length("32").build();

Here, the truck instance is actually created at builder's creation time. Then, builder methods fill the eagerly created instance with attributes, one by one.

Whether to use one or the other, is up to you. Plase let me know if this has errors, as I couldn't test it, as well as if you have any questions (I'm so used to this kind of code that I might be lacking some useful explanation).

这篇关于生成器模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆