是这个例子中真正需要的装饰模式吗?

时间:2015-09-03 17:31:26

标签: design-patterns

这是问题陈述

一家餐厅有4个披萨基地:

全麦比萨饼 木火披萨
奶酪填充比萨饼 薄皮披萨

有8个浇头:

番茄,洋葱,奶酪,Pepporoni,辣椒,大蒜,芝士,土豆,

计算比萨饼的价格,给予比萨饼基础和0或更多的配料(假设每个基础和顶部配置了一些价格)。

我的伪代码解决方案是:

    public class Pizza {
        public int getPrice(base, String... toppings){
            PizzaBaseMap.get(base) + toppingsMap.sum(t -> toppingsMap.get(t))
        }



    Hashmap<String, int> PizzaBaseMap= {
        whole_wheat : 1
        wood_fire : 2
        cheese_filled : 2
        thin_crust : 4
    }

    Hashmap<String, int> toppingsMap = {
        tomato : 1
        onion : 2
        cheese : 4
        pepporoni : 5
        capsicum : 2
        garlic : 2
        paneer : 4
        potato : 4
    }

    //client Code 

    int Price = new Pizza().getPrice("whole_wheat", ["tomato", "cheese"])

我是否真的需要像Headfirst desgin模式书中那样使用装饰器?具有装饰器模式的解决方案如下所示:

    public interface iPizza
        {
             double cost();
        }

    //Decorator
    public interface iToppingDecorator:iPizza
    {
    }

    //Pizza types
    class WholeWheatPizza:iPizza
        {
            public double cost()
            {

            }
        }

    class WoodFire : iPizza
        {
            public double cost()
            {

            }
        }

     class CheeseFilled : iPizza
        {
            public double cost()
            {

            }
        }

      class Thincrust : iPizza
        {
            public double cost()
            {

            }
        }

    //Toppings inheriting Decorator Interface

    class CheeseTopping:iToppingDecorator
        {
             iPizza pizza;
             public CheeseTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost(); 
            }
        }

     class TomatoTopping:iToppingDecorator
        {
            iPizza pizza;
            public TomatoTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost();
            }
        }

     class OnionTopping:iToppingDecorator
        {
            iPizza pizza;
            public OnionTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost();
            }
        }

     class PepporoniTopping:iToppingDecorator
        {
            iPizza pizza;
            public PepporoniTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost();
            }
        }

     class CapsicumTopping:iToppingDecorator
        {
            iPizza pizza;
            public CapsicumTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost();
            }
        }

     class PaneerTopping:iToppingDecorator
        {
            iPizza pizza;
            public PaneerTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost();
            }
        }

     class GarlicTopping:iToppingDecorator
        {
            iPizza pizza;
            public GarlicTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost();
            }
        }

     class PotatoTopping:iToppingDecorator
        {
            iPizza pizza;
            public PotatoTopping(iPizza pizzatype)
            {
                this.pizza = pizzatype;
            }

            public double cost()
            {
                return <price> + pizza.cost();
            }
        }

    //client
    static void Main()
            {
                iPizza pizza1 = new WholeWheatPizza();
                pizza1 = new CheeseTopping(pizza1);

                Console.WriteLine("Pizza 1 cost: "+pizza1.cost()+"INR");

                iPizza pizza2 = new WoodFire();
                pizza2 = new CheeseTopping(pizza2);
                pizza2 = new TomatoTopping(pizza2);
                Console.WriteLine("Pizza 2 cost: " + pizza2.cost() + "INR");

                Console.ReadLine();

            }

我觉得它是一个完全矫枉过正的,我的代码与装饰模式的解决方案一样可扩展。有什么想法吗?

1 个答案:

答案 0 :(得分:5)

是的,在这种情况下可能有点过头了。然而,想象一个更大的应用程序,有更多的浇头;或者价格修饰者更复杂的地方;或者您正在构建可重用的库。

在这些情况下,装饰器模式有两个优势:

  1. 您可以单独添加和删除代码库中的装饰器,因为每个装饰器的行为都包含在其类中。
  2. 可以基于每个装饰器自定义对象的装饰方式
  3. 例如,假设我们有特别优惠,其中任何披着凤尾鱼的披萨都是20%的折扣(不要吃凤尾鱼!)。使用哈希映射这非常麻烦:

    foreach (var topping in toppings)
        if (topping is Anchovies)
            price := price * 0.8
        else
            price := price - toppingCosts[topping]
    

    使用装饰器模式,我们可以添加一个新类:

    class AnchoviesToppingDecorator : IPizzaToppingDecorator
    {
        private IPizza pizza;
    
        public AnchoviesTopping(IPizza pizza)
        {
            this.pizza = pizza;
        }
    
        public double cost()
        {
            return this.pizza.Cost() * 0.8f;
        }
    }
    

    当然,装饰器应用程序的顺序变得很重要。如有必要,可以使用类型系统强制执行此操作。

    您可以使用类型系统强制装饰器上的订单。假设所有顶部装饰器必须在折扣装饰器之前。在我们的系统中,装饰器通过它们的构造函数接受它们的前任。通过更改构造函数类型,我们可以限制我们可以构建的装饰器。

    interface IPizzaToppingDecorator : IPizzaDecorator
    {
    
    }
    
    interface IPizzaDiscountDecorator : IPizzaDecorator
    {
    
    }
    
    public class HalfPriceDecorator : IPizzaDiscountDecorator 
    {
        private IPizzaToppingDecorator pizzaToppingDecorator;
    
        public HalfPriceDecorator(IPizzaToppingDecorator pizzaToppingDecorator)
        {
            this.pizzaToppingDecorator = pizzaToppingDecorator;
        }
    
        public double Cost()
        {
            pizzaToppingDecorator.Cost() * 0.5;
        }
    }
    

    请注意构造函数如何使用IPizzaToppingDecorator而不是IPizzaDecorator。这可确保HalfPriceDecorator只能在IPizzaToppingDecorator之后应用。

    其他任何内容都会引发编译时错误;我们喜欢编译时错误,因为只有我们才能看到它们!

    在这种情况下,我认为Head First示例可以通过流畅的界面进行改进。

    能够写这个不是很好吗?

    new WholeWheatPizza().WithTomatoTopping().WithCheeseTopping().WithPepporoniTopping();
    

    这可以通过extension methods

    来实现
    public static IPizza WithCheeseTopping(this IPizza pizza)
    {
        return new CheeseToppingDecorator(pizza)
    }