装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。下面先演示一个基本的装饰模式实例,然后我们用unity方式来实现。
下面的示例是模仿日常的房屋装修,装修过程中需要安装水管,电线,铺地板以及刷墙壁等操作,但是每个人选择的顺序可以不相同。这种场景正好适合使用装饰者模式,操作是固定的,但是顺序是变化的。
基本代码如下:
namespace DecorateTest { public interface IDecorate { void RoomDecorate(); } }
ElecDecorator.cs
using System; namespace DecorateTest { public class ElecDecorator : IDecorate { IDecorate decorator = null; public ElecDecorator(IDecorate decorator) { this.decorator = decorator; } public void RoomDecorate() { Console.WriteLine("安装电线"); if (decorator != null) decorator.RoomDecorate(); } } }
FloorDecorator.cs
using System; namespace DecorateTest { public class FloorDecorator : IDecorate { IDecorate decorator = null; public FloorDecorator(IDecorate decorator) { this.decorator = decorator; } public void RoomDecorate() { Console.WriteLine("铺地板"); if (decorator != null) decorator.RoomDecorate(); } } }
WallDecorator.cs
using System; namespace DecorateTest { public class WallDecorator : IDecorate { public void RoomDecorate() { Console.WriteLine("粉刷墙壁,装修完毕"); } } }
WaterDecorator.csWaterDecorator.cs
using System; namespace DecorateTest { public class WaterDecorator : IDecorate { IDecorate decorator = null; public WaterDecorator(IDecorate decorator) { this.decorator = decorator; } public void RoomDecorate() { Console.WriteLine("安装水管"); if (decorator != null) decorator.RoomDecorate(); } } }
Program.cs
using System; using Microsoft.Practices.Unity; namespace DecorateTest { class Program { static void Main(string[] args) { WallDecorator wad = new WallDecorator(); //刷墙 ElecDecorator ed = new ElecDecorator(wad); //刷墙前安装电线 WaterDecorator wd = new WaterDecorator(ed); //安装电线前安装水管 FloorDecorator fd = new FloorDecorator(wd); //安装水管前铺地板 fd.RoomDecorate(); Console.Read(); } } }
运行结果:
下面我们使用unity api来实现这个功能,首先我们必须注册这四个操作对应的类到容器中
container.RegisterType<IDecorate, WallDecorator>(); container.RegisterType<IDecorate, ElecDecorator>("ele"); container.RegisterType<IDecorate, WaterDecorator>("water"); container.RegisterType<IDecorate, FloorDecorator>("floor");
三个命名实例和一个默认的匿名实例。为了模拟基本示例中的装修顺序,我们首先要铺地板i,那么就需要创建FloorDecorator的实例,方法很简单
IDecorate de = container.Resolve<IDecorate>("floor"); de.RoomDecorate();
运行结果:
分析流程,我们首先解析IDecorate的名为为"floor"的实例,于是根据映射配置,会创建FloorDecorator类的对象,但是该类的构造函数需要一个IDecorate类型的参数,由于我们定义了默认的匿名实例映射,于是容器就会创建一个WallDecorator类的对象,并作为参数传递给FloorDecorator类的构造函数。最后调用装修方法时,先调用FloorDecorator类中定义的实现,然后调用WallDecorator类中定义的实现。
接下来是关键步骤,因为我们不希望铺完地板后就刷墙面,而是希望安装水管,怎么办呢?关键在于Fl oorDecorator类的构造函数中使用的参数不能是IDecorate的匿名实例,而是要使用名称为"water"的IDecorate的命名实例。简单的说,我们需要重写构造函数中名称为“decorator”的参数。实现方式就是使用ParameterOverride方法,他可以重写指定名称的参数,并提供参数值。具体的代码下面:
IDecorate de = container.Resolve<IDecorate>("floor", new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("water"))); de.RoomDecorate();
运行结果:
出现了我们没有预料的StackOverflowException-----堆栈溢出异常。其原因在于ResolvedParameter会将解析实例过程中使用到的所有名称为"decorator",类型为IDecorate的参数都进行重写。这样就会导致,在创建一个WaterDecorator对象A的时候要创建一个新的WaterDecorator对象实例B作为参数给到A,但是在创建B的时候又需要一个新的WaterDecorator对象实例C作为参数,这样就会无限递归下去,最终导致堆栈溢出。
如何解决?必须通知容器,参数重写的范围,只是重新FloorDecorator类中构造函数中的名称为"decorator",类型为IDecorate的参数。实现方式如下:
IDecorate de = container.Resolve<IDecorate>("floor", new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("water")).OnType<FloorDecorator>()); de.RoomDecorate();
我们利用OnType<>扩展方法来限制使用参数重写的类型。运行结果如下:
最后我们在安装完水管后安装电线,实现方式和上面类似。通知容器,在创建WaterDecorator类型实例的时候,不同默认的IDecorate的匿名实例WallDecorator,重写为IDecorate的命名为"ele"的命名实例ElecDecorator。
IDecorate de = container.Resolve<IDecorate>("floor", new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("water")).OnType<FloorDecorator>(), new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("ele")).OnType<WaterDecorator>()); de.RoomDecorate();
运行结果如下:
完整代码:
static void Main(string[] args) { #region 基本示例 //WallDecorator wad = new WallDecorator(); //刷墙 //ElecDecorator ed = new ElecDecorator(wad); //刷墙前安装电线 //WaterDecorator wd = new WaterDecorator(ed); //安装电线前安装水管 //FloorDecorator fd = new FloorDecorator(wd); //安装水管前铺地板 //fd.RoomDecorate(); #endregion using (IUnityContainer container = new UnityContainer()) { container.RegisterType<IDecorate, WallDecorator>(); container.RegisterType<IDecorate, ElecDecorator>("ele"); container.RegisterType<IDecorate, WaterDecorator>("water"); container.RegisterType<IDecorate, FloorDecorator>("floor"); IDecorate de = container.Resolve<IDecorate>("floor", new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("water")).OnType<FloorDecorator>(), new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("ele")).OnType<WaterDecorator>()); de.RoomDecorate(); } Console.Read(); }
下面这段代码,你猜猜结果如何?
IDecorate de = container.Resolve<IDecorate>("water", new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("floor")).OnType<ElecDecorator>(), new ParameterOverride("decorator", new ResolvedParameter<IDecorate>("ele")).OnType<WaterDecorator>()); de.RoomDecorate();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
运行结果,你猜对了吗?