代码改变世界

装饰者模式-Decker

2011-07-06 23:55  三皮开发时  阅读(228)  评论(0编辑  收藏  举报

举例说明该模式的应用场:

比如星巴兹咖啡,咖啡由饮料+调料组成,不同的饮料,调料价格也有所不同,并且客户的需求各有不同,调料的份数也各不相同,在这种需求变化较大的情况下如何设计一个结构能很好的解决星巴兹咖啡的烦恼(计算用户咖啡费用)

分析:

 

结合代码:

Beverage:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DeckerMode.Base
{
    
/// <summary>
    
/// 饮料抽象类
    
/// </summary>
    public abstract class Beverage
    {
        
protected string description = "Unknown Beverage";
        
/// <summary>
        
/// 获取描述信息
        
/// </summary>
        
/// <returns></returns>
        public virtual string GetDescription()
        {
            
return description;
        }

        
/// <summary>
        
/// 消费
        
/// </summary>
        
/// <returns></returns>
        public abstract double Coast();
    }
}

 

 CondimentDecorator:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DeckerMode.Base
{
    
/// <summary>
    
/// 调料类
    
/// </summary>
    public abstract class CondimentDecorator:Beverage
    {
        
public override abstract string GetDescription();
    }
}

 

Espresso:浓缩咖啡
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DeckerMode.Base;

namespace DeckerMode.Coffee
{
    
/// <summary>
    
/// 浓缩咖啡
    
/// </summary>
    public class Espresso:Beverage
    {
        
public Espresso()
        {
            description 
= "Espresso";
        }


        
public override double Coast()
        {
            
return 1.99;
        }
    }
}

 

HouseBlend:家常咖啡

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DeckerMode.Base;

namespace DeckerMode.Coffee
{
    
/// <summary>
    
/// 家常咖啡
    
/// </summary>
    public class HouseBlend:Beverage
    {
        
public HouseBlend()
        {
            description 
= "House Blend Coffee";
        }

        
public override double Coast()
        {
            
return 0.89;
        }
    }
}

 

Mocha:摩卡调料

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DeckerMode.Base;

namespace DeckerMode.Condiment
{
    
public class Mocha:CondimentDecorator
    {
        Beverage beverage;

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

        
public override string GetDescription()
        {
            
//加上摩卡调料标记
            return beverage.GetDescription() + ",Mocha";
        }

        
//装饰+被装饰者价格
        public override double Coast()
        {
            
return 0.20 + beverage.Coast();
        }
    }
}

 

Soy:豆浆调料

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DeckerMode.Base;

namespace DeckerMode.Condiment
{
    
/// <summary>
    
/// 
    
/// </summary>
     public class Soy:CondimentDecorator
    {
         Beverage beverage;

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

         
//加上豆浆调料标记
         public override string GetDescription()
         {
             
return beverage.GetDescription() + ",Soy";
         }
         
         
public override double Coast()
         {
             
return 0.3 + beverage.Coast();
         }
    }
}

 

Whip:调料

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DeckerMode.Base;

namespace DeckerMode.Condiment
{
    
public class Whip:CondimentDecorator
    {
        Beverage beverage;

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

        
public override string GetDescription()
        {
            
//加上whip标记
            return beverage.GetDescription() + ",Whip";
        }

        
public override double Coast()
        {
            
return 0.1 + beverage.Coast();
        }
    }
}

 

Main:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DeckerMode.Base;
using DeckerMode.Coffee;
using DeckerMode.Condiment;

namespace DeckerMode
{
    
class Program
    {
        
static void Main(string[] args)
        {
            Beverage beverage 
= new Espresso();
            Console.WriteLine(beverage.GetDescription()
+" $"+beverage.Coast());

            Beverage beverage2 
= new HouseBlend();
            beverage2 
= new Mocha(beverage2);
            beverage2 
= new Mocha(beverage2);
            beverage2 
= new Whip(beverage2);
            beverage2 
= new Soy(beverage2);
            Console.WriteLine(beverage2.GetDescription()
+" $"+beverage2.Coast());
        }
    }
}

 

运行结果图:

 

总结:

 

设计原则:

1. 多用组合,少用继承。(装饰者用的就是组合,虽然表面上看上去是继承得到的方法,其实不然,子类继承只是得到相同的类型,根据自己的需要自由的组合,自由的装饰,也就是我们所说的动态添加对象/功能,灵活)

利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。

2. 类应设计的对扩展开放,对修改关闭。

 

 

要点:

1. 装饰者和被装饰对象有相同的超类型。

2. 可以用一个或多个装饰者包装一个对象。

3. 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。

4. 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。

5. 装饰模式中使用继承的关键是想达到装饰者和被装饰对象的类型匹配,而不是获得其行为。

6. 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。在实际项目中可以根据需要为装饰者添加新的行为,做到“半透明”装饰者。

7. 适配器模式的用意是改变对象的接口而不一定改变对象的性能,而装饰模式的用意是保持接口并增加对象的职责。

 

 

适用性:

以下情况使用Decorator模式

1. 需要扩展一个类的功能,或给一个类添加附加职责。

2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。

3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。

4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

 

 

优点:

1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。

2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

缺点:

1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。

2. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。

3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。