![translation](https://cdn.durumis.com/common/trans.png)
Ez egy AI által fordított bejegyzés.
[Hatékony Java] 2. elem: Ha a konstruktornak sok paramétere van, fontolja meg a Builder használatát
- Írás nyelve: Koreai
- •
-
Referencia ország: Minden ország
- •
- Informatika
Válasszon nyelvet
A durumis AI által összefoglalt szöveg
- A sok paraméterrel rendelkező konstruktorok hatékony kezelésére szolgáló különböző mintákat mutat be, és összehasonlítja a fokozatos konstruktor minta, a Java Bean minta és a Builder minta előnyeit és hátrányait.
- A Builder minta előnye, hogy növeli a kód olvashatóságát és stabilitását, és különösen hatékonyan használható hierarchikusan tervezett osztályokkal.
- A Builder minta segítségével a kódot egyértelműbbé lehet tenni, de hátrányai közé tartozik a Builder objektum létrehozása és a kód hosszának növekedése.
Fokozatos építő minta
A statikus gyár és a konstruktorok mind nehézkesek a sok paraméter kezelésében. Tegyük fel, hogy a osztálynak 6 mezője van, és külön konstruktort szeretnénk a paraméterek 2-es, 3-as, … számú beállításához. Ekkor használhatjuk a fokozatos építő mintát, ahogy az alábbiakban látható.
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);
}
De még így is, ha sok paraméterünk van, a kód olvasásakor nehéz lehet megérteni, hogy melyik érték mire vonatkozik, és az azonos típusú paraméterek könnyen összetéveszthetők, amikor az értékeket beállítjuk.
Java Bean minta
A Java Bean minta a paraméter nélküli konstruktort használja az objektum létrehozásához, majd a setter metódusokat hívja meg a kívánt paraméterértékek beállításához.
public class NutritionFactsWithJavaBeansPattern {
private int servingSize = -1; // Kötelező. Nincs alapértelmezett érték.
private int servings = -1; // Kötelező. Nincs alapértelmezett érték.
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;
}
A Java Bean minta lehetővé teszi az objektumok könnyű létrehozását még sok paraméter esetén is, de több setter metódust kell meghívni az objektum létrehozásához, és az objektum nem lesz teljesen konzisztens, amíg nem hívjuk meg az összes setter metódust. Emiatt az osztály nem tehető megváltoztathatatlanná.
Építő minta
Általában az építő mintát használjuk, amely ötvözi a fokozatos építő minta stabilitását és a Java Bean minta olvashatóságát.
Az ügyfél nem hozza létre közvetlenül a szükséges objektumot, hanem a kötelező paraméterekkel létrehoz egy építő objektumot. Ezután az építő objektum által biztosított, setter metódusokhoz hasonló metódusokkal állítja be a kívánt opcionális paramétereket. Végül meghívja a paraméter nélküli build() metódust a szükséges objektum létrehozásához.
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);
}
}
Az Építő osztályon belüli konstruktort csak a kötelező paraméterekkel hívjuk meg, a többi opcionális paramétert setter metódusokkal töltjük ki. Végül a build() metódus létrehoz egy teljes NutritionFactsWithBuilderPattern objektumot. A NutritionFactsWithBuilderPattern osztály megváltoztathatatlan, és az építő setter metódusai önmagukat adják vissza, így láncolhatóan hívhatók. Ezt a módszert folyékony API-nak vagy metódusláncolásnak nevezik.
NutritionFactsWithBuilderPattern nutritionFacts =
new NutritionFactsWithBuilderPattern.Builder(240, 8)
.calories(100)
.sodium(35)
Az ügyfél szempontjából az építő minta egyszerűbbé teszi a kód írását és olvasását.
Az építő minta jól működik a hierarchikusan felépített osztályokkal
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 Pizza.Builder osztály rekurzív típuskorlátozást használó generikus típus, és a self() nevű absztrakt metódus segítségével támogatja a metódusláncolást az alosztályok szempontjából típuskonverzió nélkül. Az alosztályokban ennek az absztrakt metódusnak az értékét a saját típusra kell állítani.
Nézzük meg a Pizza alosztályait, a New York-i pizzát és a calzone pizzát, és tapasztaljuk meg az építő minta rugalmasságát.
public class NyPizza extends Pizza {
public enum Size {
SMALL, MEDIUM, LARGE
}
private final Size size; // Kötelező paraméter
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; // Opcionális paraméter
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;
}
}
Az egyes alosztályok építője által definiált build() osztály konkrétan az alosztályt adja vissza. Az alosztály metódusainak az a képessége, hogy az alosztály típusát adják vissza a szuperosztály metódusai által visszaadott típus helyett, kovariáns visszatérési típusnak nevezik. Ezzel a funkcióval az ügyfeleknek nem kell típuskonverziót végrehajtaniuk.
NyPizza nyPizza = new NyPizza.Builder(Size.SMALL)
.addTopping(Topping.SAUSAGE)
.addTopping(Topping.ONION)
.build();
CalzonePizza calzonePizza = new CalzonePizza.Builder()
.addTopping(Topping.HAM)
.sauceInside()
Az ügyfelek szempontjából a Pizza enum és az egyes alosztályok enumai felcserélhetők, és a megfelelő metódusokkal hozhatnak létre teljes objektumokat.
Az építő minta hátrányai
- Építő objektumot kell létrehozni.
- A kód terjedelmes.
Összefoglalás
Ha a konstruktornak vagy a statikus gyár metódusnak sok paramétert kell kezelnie, vegye figyelembe az építő mintát.
Forrás
- Efektiv Java