装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。下面先演示一个基本的装饰模式实例,然后我们用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();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
运行结果,你猜对了吗?