2015-12-02 72 views
2

我在學習裝飾設計模式。在這個例子中,我創建了一個應用程序,根據咖啡的類型(意式濃縮咖啡,咖啡豆,混合咖啡),大小(高,大,通風)和調味品裝飾(大豆,奶油,蒸牛奶) - 爲簡潔起見,排除一些代碼。從底部的輸出中可以看到,如果setSize(GRANDE)並且不包裝對象,getSize()將返回GRANDE。如果我設置大小(GRANDE),然後修飾對象,getSize()返回TALL(在Beverage類中設置的默認值)。當我修飾對象時,變量重置爲默認值

如果我setSize(GRANDE),再次裝飾對象,setSize(GRANDE),然後getSize()返回GRANDE。

注:雖然尺寸打印不正確,但成本計算正確。

問題:有沒有辦法編碼這個,所以當我setSize()它保持該值,即使對象裝飾後?

package coffee; 

public abstract class Beverage { 
    String description = "Unknown Beverage"; 
    public enum Size { TALL, GRANDE, VENTI }; 
    Size size = Size.TALL; 

    public String getDescription() { 
     return description; 
    } 

    public void setSize(Size size) { 
     this.size = size; 
    } 

    public Size getSize() { 
     return size; 
    } 

    public abstract double cost(); 
} 

package coffee; 

public abstract class CondimentDecorator extends Beverage { 
    public abstract String getDescription(); 
} 

package coffee; 

public class HouseBlend extends Beverage { 

    public HouseBlend() { 
     description = "House Blend Coffee"; 
    } 

    public double cost(){ 
     return .89; 
    } 
} 

package coffee; 

public class Soy extends CondimentDecorator { 
    Beverage beverage; 

    public Soy(Beverage beverage) { 
     this.beverage = beverage; 
    } 

    public String getDescription() { 
     return beverage.getDescription() + ", Soy"; 
    } 

    public double cost() { 
     double cost = beverage.cost(); 
     if(beverage.getSize() == Size.TALL) { 
      cost += .10; 
     } else if(beverage.getSize() == Size.GRANDE) { 
      cost += .15; 
     }else if(beverage.getSize() == Size.VENTI) { 
      cost += .20; 
     } 
     return cost; 
    } 
} 

package coffee; 
import coffee.Beverage.Size; 
public class StarbuzzCoffeeController { 

    public static void main(String[] args) { 
     Beverage beverage = new HouseBlend(); 
     System.out.println(beverage.getSize() + " " + beverage.getDescription() + " $" + String.format("%.2f", beverage.cost())); 
     beverage.setSize(Size.GRANDE); 
     System.out.println(beverage.getSize() + " " + beverage.getDescription() + " $" + String.format("%.2f", beverage.cost())); 
     System.out.println("-----------------------"); 

     Beverage beverage2 = new HouseBlend(); 
     beverage2.setSize(Size.GRANDE); 
     System.out.println(beverage2.getSize() + " " + beverage2.getDescription() + " $" + String.format("%.2f", beverage2.cost())); 

如果我不與其他的setSize按照此()它打印「高」作爲下一行的大小:

 beverage2 = new Soy(beverage2); 
     System.out.println(beverage2.getSize() + " " + beverage2.getDescription() + " $" + String.format("%.2f", beverage2.cost())); 
     System.out.println("-----------------------"); 

     Beverage beverage3 = new HouseBlend(); 
     beverage3.setSize(Size.GRANDE); 
     System.out.println(beverage3.getSize() + " " + beverage3.getDescription() + " $" + String.format("%.2f", beverage3.cost())); 
     beverage3 = new Soy(beverage3); 

如果我的setSize()我已經正確裝飾物,大小打印後再次爲GRANDE:

 beverage3.setSize(Size.GRANDE); 
     System.out.println(beverage3.getSize() + " " + beverage3.getDescription() + " $" + String.format("%.2f", beverage3.cost())); 
    } 
} 

輸出::

高層住宅混合咖啡$ 0.89

GRANDE衆議院混合咖啡$ 0.89

-----------------------

GRANDE衆議院混合咖啡$ 0.89

高層住宅混合咖啡,大豆$ 1.04

- ---------------------

GRANDE衆議院混合咖啡$ 0.89

GRANDE衆議院混合咖啡,大豆$ 1.04

回答

3

Soy類不覆蓋getSize()方法 - 因此,使用基類的默認實現。基類返回自己的size成員,該成員初始化爲TALL。

爲了解決這個問題,你就必須相應地重寫getSize()方法在Soy類:

@Override 
public Size getSize() { 
    return this.beverage.getSize(); 
} 

這將返回正確的裝飾物體的大小。

+0

此外,你可能會考慮重構你的類。目前,裝飾器正在繼承不必要的基類成員。我會在只有抽象方法(足夠描述你的(裝飾)對象的公共API)的基礎類的基礎上提出一個改變。然後創建一個抽象類來實現這些方法,這些方法有私有或受保護的成員。像HouseBlend這樣的特定實現可以擴展這個類。 – DevCybran

+0

創建另一個抽象類('BeverageDecorator'),該類實現只有一個成員的基類方法 - 「Beverage」的一個實例。所有方法都應該將調用委託給此成員。裝飾者,比如'Soy',可以擴展這個類並且只覆蓋必要的方法。 – DevCybran

0

我想你必須委託getSize()包裝/裝飾實例,如果你想要的「原」飲料的價值。

Beverage cappuccino = new Cappuccino(Size.VENTI); 
    Beverage soyMilkCappuccino = new Soy(cappuccino); 

Soy

class Soy extends Beverage { 
     private final Beverage decorated; 
     Soy(Beverage other) { this.decorated = other; } 
     // delegation 
     Size getSize() { return decorated.getSize(); } 
    } 
1

你實現Decorator模式不正確的概念。您遇到的問題來自不正確的實施。

我指下面這個維基百科文章中5個項目:https://en.wikipedia.org/wiki/Decorator_pattern

  1. 「子類原來的‘組件’類成‘裝飾’類,」
      在你的情況下,「組件」是 BeverageCondimentDecorator
    • 是裝飾
  2. 「在裝飾類,添加組件指針作爲一個字段;」
    • 將Component指針添加爲字段。您在Soy加場Beverage beverage,而不是將它添加到CondimentDecorator
  3. 「傳遞一個組件到裝飾構造函數初始化該組件的指針;」
    • 這應該CondimentDecorator以及
  4. 完成「在裝飾類,重定向所有的‘分量’的方法來‘組件’指針」
    • 你還沒有做到這一點,這是你問題的根源。在CondimentDecorator上,默認情況下,getSize()應該調用beverage.getSize()。其他領域也應該如此。

我想你應該回去什麼樣的裝飾圖案,努力實現基礎知識,並牢記正確的理念重建的實現。您可能能夠以更簡單的方式解決您的問題,但是您可能對裝飾模式的實際內容有錯誤的想法。

記住最基本的原則是:

  • 飲料是組件,
  • CondimentDecorator是你的裝飾,並
  • 大豆是ConcreteDecorator
+0

謝謝ARRG。我得看看那個。我從書中得到了這個裝飾器結構和代碼:頭部設計模式(http://www.amazon.com/Head-First-Design-Patterns-Freeman/dp/0596007124)。作者有可能以一種迂迴的方式實施它,以便爲像我這樣的初學者虛擬它。 –

0

以下固定的getSize ()問題(我仍在努力瞭解如何重構此代碼):

在CondimentDecorator類中添加了抽象方法getSize:

package coffee; 

public abstract class CondimentDecorator extends Beverage { 
    public abstract String getDescription(); 
    public abstract Size getSize(); 
} 

覆蓋concreteDecorator類中的getSize()。例如:

package coffee; 

public class Soy extends CondimentDecorator { 
    Beverage beverage; 

    public Soy(Beverage beverage) { 
     this.beverage = beverage; 
    } 

    public String getDescription() { 
     return beverage.getDescription() + ", Soy"; 
    } 

    public Size getSize() { 
     return this.beverage.getSize(); 
    } 

    public double cost() { 
     double cost = beverage.cost(); 
     if(beverage.getSize() == Size.TALL) { 
      cost += .10; 
     } else if(beverage.getSize() == Size.GRANDE) { 
      cost += .15; 
     }else if(beverage.getSize() == Size.VENTI) { 
      cost += .20; 
     } 
     return cost; 
    } 
} 

而且也感動的setSize()命令,這樣它的對象是裝飾之前總是出現(因爲調料成本綁大小,它會計算錯了價格,如果你改變大小後的咖啡裝飾):

public static void main(String[] args) { 
    //... 
    Beverage beverage4 = new Decaf(); 
    beverage4.setSize(Size.VENTI); //SET THE SIZE BEFORE DECORATING (COST OF CONDIMENTS DEPENDS ON SIZE) 
    beverage4 = new SteamedMilk(beverage4); 
    beverage4 = new Mocha(beverage4); 
    beverage4 = new Whip(beverage4); 
    //beverage4.setSize(Size.VENTI); //<--No, No. 
    System.out.println(beverage4.getSize() + " " + beverage4.getDescription() + " $" + String.format("%.2f", beverage4.cost())); 
} 
+0

那真的不是我的意思;) – DevCybran

+0

我的建議將適用於你的課程,如下所示:'飲料'只有抽象方法! 'BeverageBase'可能是一個新的類,其描述和大小的成員。 'HouseBlend'然後擴展'BeverageBase'。 'CondimentDecorator'可以讓飲料成員執行所有抽象方法(將這些飲料成員委託給他們)。 'Soy'不會有飲料成員(只是在構造函數中傳遞它),並且只覆蓋它想要裝飾的成員。 – DevCybran

+0

我很抱歉誤會,我會編輯我的帖子。關於你上面的評論,HouseBlend可以直接擴展飲料(省略BeverageBase類)嗎? –