JavaRush /Java Blog /Random-TW /比薩店的問題。建造者與裝飾者。
CynepHy6
等級 34
Великий Новгород

比薩店的問題。建造者與裝飾者。

在 Random-TW 群組發布

問題描述。

我們需要寫一個程式。對於想要準備不同種類披薩的披薩店:雞肉披薩、美式披薩、肉類披薩、夏威夷披薩、義大利辣香腸披薩等。我們來看看哪個模板、什麼場景適合解決這個問題。傳統上,Builder 範本用於解決「披薩問題」。還有使用Decorator模式的例子,兩種模式都是正確的,但使用上有差異。Builder 是用於建立物件的模板,而 Decorator 用於動態修改已完成的物件。 讓我們嘗試透過範例來理解這一點:

1. 模板產生器:

在這種情況下,披薩是一次用所有原料準備好的。
披薩類:
public class Pizza{ private float totalPrice = 0; private Size size; private Topping topping; private Crust crust; private Cheese cheese; public Size getSize(){ return size; } public void setSize(Size size){ this.size = size; } public Topping getTopping(){ return topping; } public void setTopping(Topping topping){ this.topping = topping; } public Crust getCrust(){ return crust; } public void setCrust(Crust crust){ this.crust = crust; } public Cheese getCheese(){ return cheese; } public void setCheese(Cheese cheese){ this.cheese = cheese; } public float getTotalPrice(){ return totalPrice; } public void addToPrice(float price){ this.totalPrice = totalPrice + price; } }
4個枚舉類別:
public enum Cheese { AMERICAN{ public float getCost(){ return 40; } }, ITALIAN { public float getCost(){ return 60; } }; public abstract float getCost(); } public enum Crust { THIN{ public float getCost(){ return 70; } } , STUFFED{ public float getCost(){ return 90; } }; public abstract float getCost(); } public enum Size { MEDIUM { public float getCost() { return 100; } }, LARGE { public float getCost() { return 160; } }; public abstract float getCost(); } public enum Topping { PEPPERONI { public float getCost(){ return 30; } }, CHICKEN{ public float getCost(){ return 35; } }, MUSHROOM{ public float getCost(){ return 20; } }; public abstract float getCost(); }
PizzaBuilder 類:
public class PizzaBuilder { Pizza pizza = new Pizza(); public PizzaBuilder withTopping(Topping topping) { pizza.setTopping(topping); pizza.addToPrice(topping.getCost()); return this; } public PizzaBuilder withSize(Size size) { pizza.setSize(size); pizza.addToPrice(size.getCost()); return this; } public PizzaBuilder withCrust(Crust crust) { pizza.setCrust(crust); pizza.addToPrice(crust.getCost()); return this; } public Pizza build() { return pizza; } public double calculatePrice() { return pizza.getTotalPrice(); } }
班級測驗:
public class PizzaBuilderTest { @Test public void shouldBuildThinCrustChickenPizza(){ Pizza pizza = new PizzaBuilder().withCrust(Crust.THIN).withTopping(Topping.CHICKEN).withSize(Size.LARGE).build(); assertEquals(Topping.CHICKEN,pizza.getTopping()); assertEquals(Size.LARGE,pizza.getSize()); assertEquals(Crust.THIN,pizza.getCrust()); assertEquals(265.0,pizza.getTotalPrice(),0); } }

2.裝飾器模板:

裝飾器模式用於動態地向物件新增或刪除附加功能,而不影響原始物件。先準備一些披薩底料,然後再加入各種配料時使用。這裡我們需要一個要裝飾的 BasePizza(基本元件)的介面(Pizza)和一個實際實作該介面的 PizzaDecorator 類別。
披薩介面:
public interface Pizza { public String bakePizza(); public float getCost(); }
BasePizza 中的實作:
public class BasePizza implements Pizza{ public String bakePizza() { return "Basic Pizza"; } public float getCost(){ return 100; } }
PizzaDecorator 類別:
public class PizzaDecorator implements Pizza { Pizza pizza; public PizzaDecorator(Pizza newPizza) { this.pizza = newPizza; } public String bakePizza() { return pizza.bakePizza(); } @Override public float getCost() { return pizza.getCost(); } }
2 個裝飾器:蘑菇和義大利辣香腸
public class Mushroom extends PizzaDecorator { public Mushroom(Pizza newPizza) { super(newPizza); } @Override public String bakePizza() { return super.bakePizza() + " with Mushroom Topings"; } @Override public float getCost() { return super.getCost()+80; } } public class Pepperoni extends PizzaDecorator { public Pepperoni(Pizza newPizza) { super(newPizza); } @Override public String bakePizza() { return super.bakePizza() + " with Pepperoni Toppings"; } @Override public float getCost() { return super.getCost()+110; } }
班級測驗:
public class PizzaDecoratorTest { @Test public void shouldMakePepperoniPizza(){ Pizza pizza = new Pepperoni(new BasePizza()); assertEquals("Basic Pizza with Pepperoni Toppings",pizza.bakePizza()); assertEquals(210.0,pizza.getCost(),0); } }

差異

諸如 Builder 和 Factory(以及 Abstract Factory)之類的模式用於建立新物件。諸如裝飾器(也稱為結構設計模式)之類的模式用於可擴展性或為已建立的物件提供結構變更。兩種類型的模式主要透過繼承來促進組合,且差異不足以保證使用建構器而不是裝飾器。兩者在執行時都會給予自己的行為,而不是繼承它。在一種情況下,如果我們想限制具有某些屬性/功能的物件的創建,最好使用建構器。例如,在創建物件之前必須設定 4-5 個屬性,或者我們希望凍結物件的創建,直到設定某些屬性為止。簡而言之,使用它而不是建構函式 - 正如 Joshua Bloch 在《Java:有效程式設計》第二版中所寫。建構器提供了產生的物件應具有的屬性,但隱藏瞭如何設定它們。裝飾器用於在基於現有物件建立新物件時向現有物件新增屬性。在新增物件的所有功能時,凍結物件沒有任何限制。兩個模板都使用合成並且可能看起來相似。主要區別在於它們的用途。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION