Unity Application Block 学习笔记 之 装饰模式应用

本文介绍了装饰模式的概念,通过Unity Application Block展示了如何在不修改原有代码的基础上,动态扩展对象功能。文章以房屋装修为例,详细解释了如何使用Unity API注册和解析类,以及如何通过ParameterOverride和OnType<>方法解决重写参数时可能出现的堆栈溢出问题。文章逐步演示了从铺地板到安装水电的过程,最后讨论了代码的运行结果。
摘要由CSDN通过智能技术生成

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

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

运行结果,你猜对了吗?

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值