4 回答

TA貢獻1836條經驗 獲得超3個贊
裝飾器是擴展另一個類的功能的類。裝飾器通常實現相同的接口,以便可以使用修飾的對象而不是基本對象。一個很好的例子是應用于文件或更一般地說,應用于數據流實現的壓縮器和/或加密器,如@nits.kk的答案所示。
在披薩的例子中,我們應該定義我們需要的行為:
public interface Pizza {
public String getIngredients(); // comma separated
public double getTotalPrice();
}
比薩餅由兩種主要類型的成分組成:強制性的單一基料和可選的多種配料。每種成分都有自己的價格。
public class PizzaIngredient {
private double getPrice() {
return 0.0;
}
}
披薩基地本身就是最簡單的披薩,因此它必須實現接口。它有一個大小作為其屬性(當然還有價格)。我們可以將一個大小實現為一個單獨的類,但我不認為它是合理的 - 它不夠通用,無法在披薩世界之外有用,也不夠復雜,不值得擁有自己的接口。Pizza
public class PizzaBase extends PizzaIngredient implements Pizza {
public PizzaBase(String size) {
this.size = size;
}
public String getIngredients() {
return size + " base"; // the only ingredient is this base
}
public double getTotalPrice() {
return getPrice(); // the base-only pizza costs the base cost
}
private double getPrice() {
if(size == "small")
return 2.0;
if(size == "medium")
return 2.5;
return 3.0; // large and undefined
}
private final String size;
}
現在我們需要澆頭。它們將被添加到比薩餅的頂部作為裝飾器:比薩餅加澆頭也是比薩餅,所以最上面的頂部將代表整個構圖。這種比薩餅的配料表是基礎比薩餅的成分清單加上其最上面的澆頭的名稱。同樣,總價也是如此。
public class PizzaTopping extends PizzaIngredient implements Pizza {
public PizzaTopping(String name, Pizza pizza) {
this.name = name;
this.pizza = pizza;
}
public String getIngredients() {
return pizza.getIngredients() + ", " + getName();
}
public double getTotalPrice() {
return pizza.getTotalPrice() + getPrice();
}
public String getName() {
return name;
}
private final String name;
private final Pizza pizza;
}
讓我們定義一些混凝土澆頭:
public class MozzarellaTopping extends PizzaTopping {
public MozzarellaTopping(Pizza pizza) {
super("mozzarella", pizza);
}
private double getPrice() {
return 0.5;
}
}
public class MushroomTopping extends PizzaTopping {
public MushroomTopping(Pizza pizza) {
super("mushroom", pizza);
}
private double getPrice() {
return 2.0;
}
}
public class PepperoniTopping extends PizzaTopping {
public PepperoniTopping(Pizza pizza) {
super("pepperoni", pizza);
}
private double getPrice() {
return 1.5;
}
}
public class GreenOliveTopping extends PizzaTopping {
public GreenOliveTopping(Pizza pizza) {
super("green olive", pizza);
}
private double getPrice() {
return 1.2;
}
}
好吧,這是很多課程;但是我們什么時候需要它們中的哪一個?
在這里,一個工廠加入了團隊。工廠是用于創建某些類的對象的類。它用于在幕后隱藏創建細節,特別是當創建的對象很復雜或具有不同的具體類時。當生成的對象創建為獨立實體時,工廠類可以只是一個包含靜態方法的命名空間。OTOH,如果對象是在某個上下文中創建的,則工廠可以是與上下文關聯的對象(例如,參數化),并在創建過程中使用該上下文。
我們可以根據用戶輸入,使用工廠隨意制作比薩餅配料。大多數配料都是澆頭,必須涂在已經存在的比薩餅之上,所以讓我們把比薩餅送到工廠,這樣就可以收到裝飾好的比薩餅。特殊情況是創建比薩餅底,該比薩餅底不應用于另一種比薩餅;在這種情況下,參數被忽略,因此我們可以傳遞 。pizzanull
public class PizzaFactory {
public static Pizza getPizza(Pizza pizza, String name)
{
if ( name.equals("small") || name.equals("medium") || name.equals("large") )
return new PizzaBase(name);
else if ( name.equals("mozzarella") )
return new MozzarellaTopping(pizza); // add topping to the pizza
else if ( name.equals("mushroom") )
return new MushroomTopping(pizza);
else if ( name.equals("pepperoni") )
return new PepperoniTopping(pizza);
else if ( name.equals("green olive") )
return new GreenOliveTopping(pizza);
return null;
}
}
現在我們準備制作我們的比薩餅。
class PizzaTest {
public static void main(String[] args) {
DecimalFormat priceFormat = new DecimalFormat("#.##");
Pizza pizza;
pizza = PizzaFactory.getPizza(null, "small");
System.out.println("The small pizza is: " + pizza.getIngredients());
System.out.println("It costs " + priceFormat.format(pizza.getTotalCost()));
pizza = PizzaFactory.getPizza(null, "medium");
pizza = PizzaFactory.getPizza(pizza, "mozzarella");
pizza = PizzaFactory.getPizza(pizza, "green olive");
System.out.println("The medium pizza is: " + pizza.getIngredients());
System.out.println("It costs " + priceFormat.format(pizza.getTotalCost()));
String largePizzaOrder[] = { "large", "mozzarella", "pepperoni",
"mushroom", "mozzarella", "green olive" };
pizza = null;
for (String cmd : largePizzaOrder)
pizza = PizzaFactory.getPizza(pizza, cmd);
System.out.println("The large pizza is: " + pizza.getIngredients());
System.out.println("It costs " + priceFormat.format(pizza.getTotalCost()));
}
}
警告:上面的代碼中有一些陷阱和快捷方式。
最重要的是缺乏對輸入的驗證:當意外的命令到達時,工廠將返回,這將導致將來使用或 時崩潰。nullgetIngredients()getTotalCost()
另一個是將價格硬編碼為具體的類。實際的解決方案必須使用一些價目表,并在成分創建時(并將提取的價格存儲在成分對象中)或使用時獲取價格,即在方法中(這需要從比薩餅的成分中訪問價格表)。getCost()

TA貢獻1866條經驗 獲得超5個贊
讓我們看看下面的幾點開始
裝飾者圖案以其最純粹的形式打算.
enhance existing behavior of an object at run time without destroying the existing interface of the object
裝飾意味著增強物體的現有行為。
裝飾對象具有與被修飾的基本對象相同的(基本)接口。
問題 : 對象從其類(編譯時)中分離出來?,F在,您將如何繼續增強行為?
答:通過使用裝飾器模式也稱為包裝器。
例如:你有一個可以加密的文件,假設加密的方法目前是5個,結果將是加密的文件。可以再次加密加密加密的文件。此外,讓我們假設有5種方法可以壓縮文件,以后也可以增加。一個文件可以通過方法EA加密,然后可以通過方法ZA壓縮,然后再次可以通過方法EB加密,類似的序列可以產生不同的結果文件。
其中一個好方法是如下。
public class TextFile{
public void create(){/*somecode*/};
public void format(){//code for default plain text};
}
public class AEncryptedFile extends TextFile{
private TextFile wrapped;
public AEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for encryption type A
}
}
public class BEncryptedFile extends TextFile{
private TextFile wrapped;
public BEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for encryption type B
}
}
public class AZippedFile extends TextFile{
private TextFile wrapped;
public BEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for zip type A
}
}
public class BZippedFile extends TextFile{
private TextFile wrapped;
public BEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for zip type B
}
}
public void UserClass{
public static void main(String[] args){
TextFile file = new BZippedFile(new AEncryptedFile(new TextFile()));
file.format();
}
在上面的示例代碼中,可以說
TextFile的一個對象已被AEncryptedFile對象修飾(通過包裝),該對象由BZippedFile進一步修飾,在每個這些裝飾中,對現有對象進行了額外的增強
這樣,TextFile的現有對象可以在運行時傳遞給各種方法,并且可以通過將其包裝在TextFile子類型的另一個對象中來修飾該對象。
注意:裝飾器模式實現具有LinkedList的結構。

TA貢獻1824條經驗 獲得超6個贊
您對裝飾器圖案目的的期望/理解可能略有不同。裝飾器模式旨在包裝一組現有功能,以便除了已經存在的功能之外,還提供一些新功能。
一個更好的披薩示例是參加披薩課程,它可以執行以下操作:
提供比薩餅
提供軟飲料
然后嘗試添加可以供應沙拉的功能。因此,此披薩類的簡化版本可能如下所示:
public class Pizzeria {
public String orderPizza() {
System.out.println("you ordered a pizza");
}
public String orderSoftDrink() {
System.out.println("you ordered a soft drink");
}
}
為了在這里實現裝飾器模式,我們包裝現有的類,然后添加一些新函數公共字符串訂單Pizza() { System.out.println(“you order a pizza”); } public String orderSoftDrink() { System.out.println(“you order a soft drink”); } ality:Pizzeria
public class NewPizzeria {
private Pizzeria pizzeria;
public NewPizzeria() {
pizzeria = new Pizzeria();
}
public String orderPizza() {
pizzeria.orderPizza();
}
public String orderSoftDrink() {
pizzeria.orderSoftDrink();
}
public String orderSalad() {
System.out.println("you ordered a salad");
}
}
這里的關鍵點是類“擁有”自己的對象。在大多數情況下,它只是重新定位已經具有的相同功能。但是,它還增加了一些自己的新功能。NewPizzeriaPizzeriaPizzeria
裝飾器設計模式在已經存在一個類的情況下很有用,該類主要適合您的需求,但是您需要其他東西并且您也無法重寫該類(例如,因為它是某些庫的一部分)。在這種情況下,包裝該類并使用裝飾器模式是一個不錯的選擇。

TA貢獻1854條經驗 獲得超8個贊
概念方面,在裝飾器模式中,一個處理的輸出作為另一個處理的輸入。
因此,在您的情況下,它應該像這樣:
getToppingFoo(getToppingBar(...(getXBaseSizePizzaCost())
它解析為:
FooToppingCost + (BarToppingCost + ... ( Cost of pizza with base of X size )
此外,您可以定義一個工廠類來獲取各種大小的對象的 Pizza,例如標準、中型、大型。無論你選擇哪種語言,邏輯都是一樣的。
添加回答
舉報