Предположим, у меня есть сборщик автомобилей с такими параметрами, как модель, цвет и скорость:
public class Car {
private String model;
private int color;
private int speed;
public Car(String model, int color, int speed) {
this.model = model;
this.color = color;
this.speed = speed;
}
//..Getters
public static class Builder {
private String model;
private int color;
private int speed;
public Builder model(String model) {
this.model = model;
return this;
}
public Builder color(int color) {
this.color = color;
return this;
}
public Builder speed(int speed) {
this.speed = speed;
return this;
}
public Car build() {
return new Car(model, color, speed);
}
}
}
А я строю машины вот так:
Car car1 = new Car.Builder()
.model("Audi")
.color(Color.RED.getRGB())
.speed(200)
.build();
Car car2 = new Car.Builder()
.model("Audi")
.color(Color.RED.getRGB())
.speed(350)
.build();
Car car3 = new Car.Builder()
.model("Audi")
.color(Color.RED.getRGB())
.speed(175)
.build();
Как видите, у меня есть параметры, которые приходится постоянно дублировать. Хотелось бы иметь возможность строить новые машины на основе некоторых существующих заготовок типа этой:
Car car1 = new Car.Builder()
.initFrom(redAudi)
.speed(200)
.build();
Car car2 = new Car.Builder()
.initFrom(redAudi)
.speed(350)
.build();
Car car3 = new Car.Builder()
.initFrom(redAudi)
.speed(175)
.build();
}
Есть ли какой-либо шаблон, который предусматривает это?
Добавьте метод initFrom(Car)
в свой Builder
класс, который вызывает все геттеры из данного экземпляра Car
и инициализирует его параметры возвращаемыми значениями. Последующие вызовы методов Builder
могут перезаписать эти значения правильными на тот момент значениями.
В качестве следующего вы можете ввести класс TemplateCar
в качестве нового родительского класса для Car
, который позволяет выполнять сборку с неполным набором параметров и имеет свой собственный Builder
(конечно, тогда вам нужно изменить вышеупомянутый метод на initFrom(TemplateCar)
).
Это то, что вы имели в виду, когда просили «Шаблон»?
Это распространенная проблема. По сути, вам нужно разоблачить asBuilder()
. Этот метод экземпляра получает построитель, который инициализируется значениями переданного экземпляра.
Car existingCar = Car.builder()......build() ;
//new car with same props as existing but the speed
Car existingCarButFaster = existingCar.asBuilder()
.speed(existingCar.speed * 2)
.build();
Реализация asBuilder()
должна быть простой.
Кроме того, вы, кажется, генерируете много шаблонов. Если подключение кодовой базы к генератору кода для вас не проблема, вы можете использовать библиотеку Lombok
, которая используется в подавляющем большинстве проектов.
/**
* If true, generate an instance method to obtain a builder that is initialized with the values of this instance
* Legal only if {@code @Builder} is used on a constructor, on the type itself, or on a static method that returns
* an instance of the declaring type.
*
* @return Whether to generate a {@code toBuilder()} method.
*/
boolean toBuilder() default false;
Спасибо за ответы! Я решил использовать решение @tquadrat, но немного расширить его. Я решил добавить интерфейс для классов, которые инициализируют сборщики.
Автомобиль.java
public class Car {
private final String model;
private final int color;
private final int speed;
public Car(String model, int color, int speed) {
this.model = model;
this.color = color;
this.speed = speed;
}
//..Getters
public static class Builder {
private String model;
private int color;
private int speed;
public Builder initFrom(@NotNull CarBuilderInitializer initializer) {
Builder builder = initializer.initBuilder(this);
this.model = builder.getModel();
this.color = builder.getColor();
this.speed = builder.getSpeed();
return this;
}
public Builder model(String model) {
this.model = model;
return this;
}
public Builder color(int color) {
this.color = color;
return this;
}
public Builder speed(int speed) {
this.speed = speed;
return this;
}
public Car build() {
return new Car(model, color, speed);
}
}}
CarBuilderInitializer.java
public interface CarBuilderInitializer {
Car.Builder initBuilder(Car.Builder builder);
}
ВашCustomBuilderCar.java
public class YourCustomBuilderCar implements CarBuilderInitializer {
@Override
public initBuilder(@NotNull Car.Builder builder) {
return builder
.model("Audi");
.color(Color.RED);
}
}
Код клиента: Автомобиль car1 = новый Car.Builder() .initFrom(новый ВашCustomBuilderCar()) .скорость(200) .строить();