2015-03-19 89 views
-1

我正在實施裝飾者設計模式。我有一個要求,裝飾者的某種組合不應該被娛樂。 示例:請參閱Head Decorator模式的首次實現:msdn article裝飾者模式應用約束

並且可以說我想限制飲料上的Mocha和Soy的組合。即摩卡或大豆,而不是兩者。我們怎麼做到這一點?

讓我知道你是否需要更多信息。

+0

這將導致裝飾的亞型之間的依賴關係,你會傷害其他OOP的設計原則......在這種情況下,裝飾將不會是一個很好的選擇 – 2015-03-19 07:39:23

+0

盡你所能做的是建築師的模式......建築師可以裝飾飲料,並可以實施這種檢查。使所有的子類型都是私有的,並且在它自己的命名空間和生成器內部/公共之中,並且你在那裏。 – 2015-03-19 07:44:57

+0

添加了一個可能的實現 – 2015-03-19 08:52:35

回答

0
在我看來

你可以這樣的方式擴展CondimentDecorator類:

public abstract class CondimentDecorator : Beverage 
{ 
    public abstract override string Description { get; } 

    public abstract override void CheckComponent(); 
} 

和實施,在這種方式使用此方法:

public class Mocha : CondimentDecorator 
{ 
    private Beverage beverage; 

    public Mocha(Beverage beverage) 
    { 
     this.beverage = beverage; 
     CheckComponent(); 
    } 



    public override double Cost() 
    { 
     return (.20 + beverage.Cost()); 
    } 



    public override string Description 
    { 
     get { return beverage.Description + ", Mocha"; } 
    } 

    public override void CheckComponent() 
    { 
     if (Description.Contains("Soy")) 
      throw new Exception("Wrong component"); 
    } 
} 
+0

但其他裝飾器之間有依賴關係......檢查是否有其他解決方案(誘導其他模式)來保持鬆散耦合。 – sanoj 2015-03-19 08:13:29

0

喜歡的東西:

namespace Beverages 
{ 
    public class Builder 
    { 
     private static Tuple<Condiments, Condiments>[] notAllowedTogether = new[] 
     { 
      new Tuple<Condiments,Condiments>(Condiments.Soy,Condiments.Mocha) 
     }; 

     public static Beverage Build(Beverages beverage, params Condiments[] condiments) 
     { 
      // check if theres a combination that is not allowed 
      if (notAllowedTogether.Any(na => condiments.Contains(na.Item1) && condiments.Contains(na.Item2))) 
      { 
       return null; // or throw exception or anything 
      } 

      // creation of the base beverage using reflection "Beverages." is the namespace the classes are defined in  
      Beverage result = (Beverage)Activator 
           .CreateInstance(
            Type.GetType("Beverages." + beverage.ToString())); 

      // adding the condiments by calling the constructors of the classes 
      foreach (Condiments condiment in condiments) 
      { 
       result = (Beverage)Type 
         .GetType("Beverages." + condiment.ToString()) 
         .GetConstructor(new Type[]{typeof(Beverage)}) 
         .Invoke(new object[]{result}); 
      } 
      return result; 
     } 
    } 

    // in order to decouple the classes from the client 
    // i have introduced enums containing the classes names 
    public enum Condiments 
    { 
     CoffeeMate, 
     Soy, 
     Mocha 
    } 

    public enum Beverages 
    { 
     Expresso, 
     HouseBlend, 
     DarkRoast 
    } 

    public abstract class Beverage 
    { 
     private string description = "Unknown Beverage"; 

     public abstract double Cost(); 

     public virtual string Description 
     { 
      get { return description; } 
     } 
    } 

    public abstract class CondimentDecorator : Beverage 
    { 
     public abstract override string Description { get; } 
    } 

    class Expresso : Beverage 
    { 
     public override double Cost() 
     { 
      return 1.99; 
     } 

     public override string Description 
     { 
      get { return "Expresso"; } 
     } 
    } 

    class HouseBlend : Beverage 
    { 
     public override double Cost() 
     { 
      return .89; 
     } 

     public override string Description 
     { 
      get { return "House Blend Coffee"; } 
     } 
    } 

    class DarkRoast : Beverage 
    { 
     public override double Cost() 
     { 
      return .99; 
     } 

     public override string Description 
     { 
      get { return "Dark Roast Coffee"; } 
     } 
    } 

    class Mocha : CondimentDecorator 
    { 
     private Beverage beverage; 

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

     public override double Cost() 
     { 
      return (.20 + beverage.Cost()); 
     } 

     public override string Description 
     { 
      get { return beverage.Description + ", Mocha"; } 
     } 
    } 

    class Soy : CondimentDecorator 
    { 
     private Beverage beverage; 

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

     public override double Cost() 
     { 
      return (.30 + beverage.Cost()); 
     } 

     public override string Description 
     { 
      get { return beverage.Description + ", Soy"; } 
     } 
    } 


    class CoffeeMate : CondimentDecorator 
    { 
     private Beverage beverage; 

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

     public override double Cost() 
     { 
      return (.15 + beverage.Cost()); 
     } 

     public override string Description 
     { 
      get { return beverage.Description + ", Coffee Mate"; } 
     } 
    } 
} 

用途和樣子:

class Program 
{ 
    static void Main(string[] args) 
    { 
     //Expresso 
     Beverage expresso = Builder.Build(Beverages.Beverages.Expresso); 
     Console.WriteLine(expresso.Description + " $" + expresso.Cost()); 

     Beverage houseBlend = Builder.Build(Beverages.Beverages.HouseBlend,Condiments.Mocha, Condiments.Soy); 

     if (houseBlend == null) 
     { 
      Console.WriteLine("Could not make that beverage"); 
     } 
     else 
     { 
      Console.WriteLine(houseBlend.Description + " $" + houseBlend.Cost()); 
     } 

     Beverage darkRoast = Builder.Build(Beverages.Beverages.DarkRoast, Condiments.CoffeeMate); 

     Console.WriteLine(darkRoast.Description + " $" + darkRoast.Cost()); 

     Console.ReadLine(); 
    } 
} 

輸出:

Expresso $1,99 
Could not make that beverage 
Dark Roast Coffee, Coffee Mate $1,14 
+0

命名空間名稱倒黴的選擇... Beverages.Beverages.Expresso,但確定=) – 2015-03-19 08:31:40

+0

仍然是一個不好的選擇。你不會從用戶那邊得到哪個組合產生錯誤。 – Fendy 2015-03-20 02:21:50

+0

@Fendy因此,在建設者的if塊的評論... – 2015-03-20 04:53:40