装饰器模式属于结构性设计模式中的一种,装饰器模式的主要为了在不修改原有类的功能的情况下,为原有的类新增功能。
为原有的类新增功能,继承也是可以实现的,当装饰器模式除了能增加新功能之外,还可以随机组合功能的顺序,而继承的实现方式,功能的执行的顺序是固定的。当然,装饰器模式也有缺点,代码实现会比继承实现方式复杂,而且不易理解和看懂。
下面用一个例子来说大家阐述一下装饰器模式(Decorator)
Product类用来实现用户产品的开通的功能,现在有个新的需求,用户在开通产品前,先检查产品是否有库存、用户账户有足够的余额开通产品,产品开通成功后,需要跟踪产品发货情况。
public class Product
{
public virtual void ProductOpen()
{
Console.WriteLine("产品开通");
}
}
根据上面的需求,我们需要增加三个功能;在开通产品前,需要增加库存检查和余额检查功能,在产品开通后,需要增加发货跟踪功能。下面我们先用装饰器模式的来实现。
第一步:先增加一个装饰器基类:BaseDecoratorProduct,如下图所示:BaseDecoratorProduct类基础Product类,一个带参数的构造函数,参数类型是Product,重写父类ProductOpen方法。
public class BaseDecoratorProduct : Product
{
protected Product _Propduct = null;
public BaseDecoratorProduct(Product product)
{
this._Propduct = product;
}
public override void ProductOpen()
{
this._Propduct.ProductOpen();
}
}
第二步:新增库存检查类RenewDecoratorProduct,如下图所示:RenewDecoratorProduct类继承BaseDecoratorProduct,一个带参数的构造函数,参数类型是Product,重写父类的ProductOpen方法,在ProductOpen方法在实现库存检测的功能,再调用父类的ProductOpen方法,如下图的base.ProductOpen()
public class RenewDecoratorProduct:BaseDecoratorProduct
{
public RenewDecoratorProduct(Product product)
: base(product)
{
}
public override void ProductOpen()
{
Console.WriteLine("检查产品是否可以开通");
base.ProductOpen();
}
}
第三步:新增库存检查类PayDecoratorProduct,如下图所示:PayDecoratorProduct类继承BaseDecoratorProduct,一个带参数的构造函数,参数类型是Product,重写父类的ProductOpen方法,在ProductOpen方法在实现账户余额检测的功能,再调用父类的ProductOpen方法,如下图的base.ProductOpen()
public class PayDecoratorProduct:BaseDecoratorProduct
{
public PayDecoratorProduct(Product product)
:base(product)
{
}
public override void ProductOpen()
{
Console.WriteLine("检查用户账号余额是否足够开通产品");
base.ProductOpen();
}
}
第四步:新增库存检查类SourseDecoratorProduct,如下图所示:SourseDecoratorProduct类继承BaseDecoratorProduct,一个带参数的构造函数,参数类型是Product,重写父类的ProductOpen方法,先调用父类的ProductOpen方法,如下图的base.ProductOpen(),再实现发货跟踪功能。
public class SourceDecoratorProduct:BaseDecoratorProduct
{
public SourceDecoratorProduct(Product product)
: base(product)
{
}
public override void ProductOpen()
{
base.ProductOpen();
Console.WriteLine("产品开发成功后,发货操作");
}
}
第五步:从第一到四步,装饰器模式的实现已经完成,接下就是调用了,调用代码如下;我们的业务逻辑是先检查库存,再检查余额,开通产品,发货,先实例化Product实例,逐步实例化库存检查,余额检测,发货跟踪,我们执行一下,看看结果。
static void Decorator()
{
Console.WriteLine("************执行开始**************");
Product product = new Product();
product = new RenewDecoratorProduct(product);
product = new PayDecoratorProduct(product);
product = new SourceDecoratorProduct(product);
product.ProductOpen();
Console.WriteLine("************执行结束**************");
Console.ReadKey();
}
执行结果如下:先出现余额检测,接着是库存检查,开通产品,发货跟踪,我们来分析一下执行结果:
装饰器子类重写的ProductOpen方法,只要在base.ProductOpen之前写的逻辑,都会在ProductOpen之前就被执行,而且执行顺序和实例化的顺序相反,即是倒过来了,但是只要在base.ProductOpen之前后写的逻辑,执行顺着则和实现话顺序相同的。到这里装饰器模式的实现基本上就讲完了。
上面的装饰器模式实现的功能,其实完全可以用一个类来继承Product类来实现的。不需要像上面那么麻烦的实现,要写那个多的类和方法,还不容易理解。如下图显示:
public class ProductExtend : Product
{
public override void ProductOpen()
{
Console.WriteLine("检查用户账号余额是否足够开通产品");
Console.WriteLine("检查产品是否可以开通");
base.ProductOpen();
Console.WriteLine("产品开发成功后,发货操作");
}
}
通过上图的继承方式,完全可以实现装饰器模式实现的功能,那我们为什么还要用装饰器模式来实现呢?这里做一个简单的解释:当你用户的渠道不一样时,库存检查,余额检测这两个功能的执行顺序不一样时,那么继承方式实现的方式就不适合了,装饰器更加适合实现,他可以根据不用的用户渠道来随机组合功能的执行顺序,或者有些用户渠道不需要检测余额时,装饰器模式就非常适合解决这类的问题,继承的方式就有心无力了。
选择什么的方式来实现,必须要结合具体的业务需求,不要为了使用设计模式使用,一定要选择合理的方式去实现。