![translation](https://cdn.durumis.com/common/trans.png)
Dit is een door AI vertaalde post.
[Effectieve Java] Item 2. Overweeg een bouwer als je constructor veel parameters heeft
- Taal van de tekst: Koreaans
- •
-
Referentieland: Alle landen
- •
- Informatietechnologie
Selecteer taal
Samengevat door durumis AI
- Er worden verschillende patronen gepresenteerd om constructors met veel parameters efficiënt te beheren, en de voor- en nadelen van het patroon van stapsgewijze constructors, het patroon van Java Beans en het builder-patroon worden vergeleken en geanalyseerd.
- Het builder-patroon heeft als voordelen dat de code leesbaarder en stabieler wordt, en is vooral effectief bij hiërarchisch ontworpen klassen.
- Het builder-patroon maakt het mogelijk om code duidelijker te schrijven, maar heeft nadelen zoals het creëren van builder-objecten en code-omvang.
Het Telkens Toenemend Constructor-Patroon
Zowel statische fabrieken als constructoren hebben het moeilijk om adequaat om te gaan met een groot aantal parameters. Stel dat een klasse 6 velden heeft en we constructoren willen maken voor 2 parameters, 3 parameters, ... dan kunnen we het Telkens Toenemend Constructor-Patroon gebruiken zoals hieronder.
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);
}
Maar zelfs dan kan het, als er veel parameters zijn, verwarrend zijn om de betekenis van elke waarde te lezen tijdens het lezen van de code, en kunnen parameters met hetzelfde type worden verward tijdens het invoegen van waarden.
JavaBeans-Patroon
Het JavaBeans-patroon maakt een object aan met een constructor zonder parameters en roept vervolgens de setter-methoden aan om de waarden van de gewenste parameters in te stellen.
public class NutritionFactsWithJavaBeansPattern {
private int servingSize = -1; // verplicht. Geen standaardwaarde.
private int servings = -1; // verplicht. Geen standaardwaarde.
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;
}
Het JavaBeans-patroon kan ervoor zorgen dat je geen waarden verwart, zelfs wanneer er veel parameters zijn. Maar om een object te maken, moet je verschillende setter-methoden aanroepen en de consistentie wordt verbroken totdat het object volledig is gemaakt. Daarom kan de klasse niet onveranderlijk worden gemaakt.
Het Builder-Patroon
Het Builder-patroon wordt meestal gebruikt om de betrouwbaarheid van het Telkens Toenemend Constructor-Patroon en de leesbaarheid van het JavaBeans-patroon te combineren.
De client maakt in plaats van het rechtstreeks maken van het benodigde object, een builder-object aan door een constructor aan te roepen met alleen de vereiste parameters. De builder-object roept vervolgens een soort setter-methoden aan om de gewenste optionele parameters in te stellen. Ten slotte roept het de build()-methode aan zonder parameters om het benodigde object te verkrijgen.
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);
}
}
De constructor in de Builder-klasse accepteert alleen de vereiste parameters, en de resterende optionele parameters worden ingevuld met een soort setter-methoden. En ten slotte wordt een voltooid NutritionFactsWithBuilderPattern-object gemaakt met de build()-methode. De NutritionFactsWithBuilderPattern-klasse is onveranderlijk en de setter-methoden van de builder retourneren de builder zelf, zodat ze in een keten kunnen worden aangeroepen. Deze aanpak wordt Fluent API of Method Chaining genoemd.
NutritionFactsWithBuilderPattern nutritionFacts =
new NutritionFactsWithBuilderPattern.Builder(240, 8)
.calories(100)
.sodium(35)
Vanuit het oogpunt van de client is het Builder-patroon eenvoudig te gebruiken en te lezen.
Het Builder-Patroon past goed bij hiërarchisch ontworpen klassen.
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();
}
De Pizza.Builder-klasse is een generiek type dat recursieve typering gebruikt en ondersteunt method chaining zonder typecasting met behulp van de abstracte self()-methode. In onderliggende klassen hoeft alleen de retourwaarde van deze abstracte methode zichzelf te zijn.
Laten we nu de subklassen van Pizza, New York Pizza en Calzone Pizza, bekijken om de flexibiliteit van het Builder-patroon te ervaren.
public class NyPizza extends Pizza {
public enum Size {
SMALL, MEDIUM, LARGE
}
private final Size size; // verplicht parameter
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; // optionele parameter
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;
}
}
De build()-klasse die in elke subklasse van de builder is gedefinieerd, retourneert de concrete subklasse. Het vermogen van de methode van de subklasse om een type te retourneren dat anders is dan het type dat wordt geretourneerd door de methode van de bovenliggende klasse, maar een subtype ervan is, wordt covariant returntype typing genoemd. Met deze functie hoeven clients geen typecasting uit te voeren.
NyPizza nyPizza = new NyPizza.Builder(Size.SMALL)
.addTopping(Topping.SAUSAGE)
.addTopping(Topping.ONION)
.build();
CalzonePizza calzonePizza = new CalzonePizza.Builder()
.addTopping(Topping.HAM)
.sauceInside()
Vanuit het oogpunt van de client kan hij de enum van Pizza en de enum van elke subklasse combineren en het object voltooien met de juiste methode voor elk.
Nadelen van het Builder-patroon
- Een builder-object moet worden gemaakt.
- De code is lang.
Samenvatting
Overweeg het Builder-patroon te gebruiken als de constructor of statische factory-methode veel parameters moet verwerken.
Bron
- Effectief Java