![translation](https://cdn.durumis.com/common/trans.png)
Esta é uma postagem traduzida por IA.
Selecionar idioma
Texto resumido pela IA durumis
- Apresenta vários padrões para gerenciar efetivamente construtores com muitos parâmetros e analisa os prós e contras dos padrões de construtor hierárquico, beans Java e construtor.
- O padrão de construtor oferece benefícios de legibilidade e estabilidade de código e é particularmente eficaz ao trabalhar com classes projetadas hierarquicamente.
- O padrão de construtor permite que você escreva código mais claro, mas tem desvantagens, como a criação de objetos de construtor e código verboso.
Padrão de construtor incremental
Fábricas estáticas e construtores têm dificuldades em lidar com um número excessivo de parâmetros. Por exemplo, se uma classe tem 6 campos e queremos criar construtores para 2, 3, ... parâmetros, podemos usar o padrão de construtor incremental, como mostrado abaixo.
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0, 0, 0, 0);
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0, 0, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
No entanto, mesmo assim, se o número de parâmetros aumentar, fica difícil entender o significado de cada valor ao ler o código e podemos confundir parâmetros com o mesmo tipo e inserir os valores incorretamente.
Padrão JavaBeans
O padrão JavaBeans cria um objeto com um construtor sem parâmetros e, em seguida, define os valores dos parâmetros desejados chamando os métodos setter.
public class NutritionFactsWithJavaBeansPattern {
private int servingSize = -1; // Necessário. Sem valor padrão.
private int servings = -1; // Necessário. Sem valor padrão.
private int calories;
private int fat;
private int sodium;
private int carbohydrate;
public void setServingSize(int servingSize) {
this.servingSize = servingSize;
}
public void setServings(int servings) {
this.servings = servings;
}
public void setCalories(int calories) {
this.calories = calories;
}
public void setFat(int fat) {
this.fat = fat;
}
public void setSodium(int sodium) {
this.sodium = sodium;
}
public void setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
}
O padrão JavaBeans pode criar uma instância sem confundir os valores, mesmo se o número de parâmetros aumentar. No entanto, para criar um objeto, precisamos chamar vários métodos setter e a coerência é quebrada até que o objeto esteja totalmente completo. Por essa razão, a classe não pode ser imutável.
Padrão de construtor
O padrão de construtor geralmente é usado porque combina a estabilidade do padrão de construtor incremental com a legibilidade do padrão JavaBeans.
Em vez de criar o objeto diretamente, o cliente chama o construtor com apenas os parâmetros necessários para obter um objeto de construtor. Então, o objeto de construtor fornece uma espécie de método setter para definir os parâmetros opcionais desejados. Finalmente, chama o método build() sem parâmetros para obter o objeto desejado.
public class NutritionFactsWithBuilderPattern {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
private NutritionFactsWithBuilderPattern(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
public static class Builder {
private final int servingSize;
private final int servings;
private int calories;
private int fat;
private int sodium;
private int carbohydrate;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
calories = val;
return this;
}
public NutritionFactsWithBuilderPattern build() {
return new NutritionFactsWithBuilderPattern(this);
}
}
O construtor dentro da classe Builder recebe apenas os parâmetros necessários e os parâmetros opcionais restantes são preenchidos por meio de uma espécie de método setter. E finalmente, o método build() é usado para criar o objeto NutritionFactsWithBuilderPattern completo. A classe NutritionFactsWithBuilderPattern é imutável e os métodos setter do construtor retornam o próprio construtor, permitindo que sejam chamados em cadeia. Esse método é chamado de API fluente ou encadeamento de métodos.
NutritionFactsWithBuilderPattern nutritionFacts =
new NutritionFactsWithBuilderPattern.Builder(240, 8)
.calories(100)
.sodium(35)
Do ponto de vista do cliente, o padrão de construtor torna o código mais fácil de escrever e ler.
O padrão de construtor combina bem com classes hierárquicas
public abstract class Pizza {
public enum Topping {
HAM, MUSHROOM, ONION, PEPPER, SAUSAGE
}
final Set toppings;
Pizza(Builder> builder) {
toppings = builder.toppings.clone();
}
abstract static class Builder> {
private EnumSet toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(topping);
return self();
}
abstract Pizza build();
protected abstract T self();
}
A classe Pizza.Builder é um tipo genérico que usa restrições de tipo recursivo e, com o método abstrato self(), permite encadeamento de métodos sem conversão de tipo nas subclasses. As subclasses podem atribuir o valor de retorno desse método abstrato a si mesmas.
Agora, vamos ver as subclasses de Pizza, a pizza de Nova York e a Calzone, para ver a flexibilidade do padrão de construtor.
public class NyPizza extends Pizza {
public enum Size {
SMALL, MEDIUM, LARGE
}
private final Size size; // Parâmetro necessário
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
public static class Builder extends Pizza.Builder {
private final Size size;
public Builder(Size size) {
this.size = size;
}
@Override
NyPizza build() {
return new NyPizza(this);
}
@Override
protected Builder self() {
return this;
}
}
}
public class CalzonePizza extends Pizza {
private final boolean sauceInside; // Parâmetro opcional
private CalzonePizza(Builder builder) {
super(builder);
sauceInside = builder.sauceInside;
}
public static class Builder extends Pizza.Builder {
private boolean sauceInside = false;
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override
CalzonePizza build() {
return new CalzonePizza(this);
}
@Override
protected Builder self() {
return this;
}
}
A classe build() definida no construtor de cada subclasse retorna a subclasse específica. A capacidade de um método de subclasse retornar um tipo diferente do tipo retornado por um método de superclasse é chamada de tipificação de retorno covariante. Com essa funcionalidade, o cliente não precisa fazer conversão de tipo.
NyPizza nyPizza = new NyPizza.Builder(Size.SMALL)
.addTopping(Topping.SAUSAGE)
.addTopping(Topping.ONION)
.build();
CalzonePizza calzonePizza = new CalzonePizza.Builder()
.addTopping(Topping.HAM)
.sauceInside()
Do ponto de vista do cliente, a enumeração Pizza e a enumeração de cada subclasse podem ser usadas em conjunto e o objeto pode ser concluído com os métodos apropriados.
Desvantagens do padrão de construtor
- Você precisa criar um objeto de construtor.
- O código é prolixo.
Resumindo
Considere o padrão de construtor se o construtor ou o método de fábrica estático precisar lidar com muitos parâmetros.
Fontes
- Eficaz Java