日期:2014-05-20  浏览次数:20743 次

每天一个设计模式(装饰模式 -2011.4.19 星期二)期待提问和讨论
装饰模式概要
顾名思义,装饰可以简单理解为旧貌新颜。即在现有基础上通过增加以达到改变。
放到开发里就是为已有的功能动态的添加更多的功能,直白一点就是
向旧有的类灵活的添加代码,以打到改变原有类的主要行为。
装饰模式的特点是新加入的东西只是为了满足一些在特定情况下才会执行的特殊行为,
把每个需要灵活使用的功能放在单独的类中,并让这个类包装他所要装饰的类,
等出现需要执行特定行为的时候,客户代码就可以在运行时根据需要灵活的有选择,有顺序的使用指定装饰类去包装对象。

因为昨天的帖子有同修提出了一个关于工资的需求,所以用装饰模式按他的需求实现了一个简单的程序结构
至于实际功能肯定是没有精力去仔细分析你的工资规则而给出完整的功能性代码,毕竟我开这贴想探讨的是如果通过
前人提出的一些方法使自己的程序能拥有一个更利于维护和扩展的结构

考虑到很多人看代码困难或不愿意细看,所以为了直观一点,代码的变量都用了中文,实际开发中这非常不可取,切勿模仿


C# code

namespace 装饰模式
{
    class 员工
    {
        public string 员工姓名;
        public double 基本工资 = 0;
        public double 出勤 = 0;
        public double 清凉 = 0;
        public double 水电 = 0;

        public 员工()
        {
        }

        public 员工(string 员工姓名)
        {
            this.员工姓名 = 员工姓名;
        }

        public virtual string 显示()
        {
            return 员工姓名;
        }
    }

    class 工资:员工
    {
        protected 员工 员工类型变量;

        public void 装饰(员工 员工类型变量)
        {
            this.员工类型变量 = 员工类型变量;
        }

        public override string 显示()
        {
            if (this.员工类型变量 != null)
            {
                return this.员工类型变量.显示();
            }
            return "";
        }
    }

    class 基础工资 : 工资
    {
        public override string 显示()
        {
            基本工资 = 3200;
            return base.显示()+"\n基本工资:"+基本工资;
        }
    }
        

    class 出勤奖:工资
    {
        public override string 显示()
        {
            出勤 = 500;
            return base.显示() + "\n出勤:" + 出勤;
        }
    }

    class 清凉补贴:工资
    {
        public override string 显示()
        {
            清凉 = 400;
            return base.显示() + "\n清凉:" + 清凉;
        }
    }

    class 水电扣除:工资
    {
        public override string 显示()
        {
            水电 = 300;
            return base.显示() + "\n水电:" + 水电;
        }
    }
}




因为最天的帖子有人提出没有具体界面,所以增加了简单的调用

C# code
namespace 装饰模式
{
    class Program
    {
        static void Main(string[] args)
        {
            员工 员工一 = new 员工("周润发");

            基础工资 基础 = new 基础工资();
            出勤奖 出勤 = new 出勤奖();
            清凉补贴 清凉 = new 清凉补贴();
            水电扣除 水电 = new 水电扣除();

            基础.装饰(员工一);
            出勤.装饰(基础);
            清凉.装饰(出勤);
            水电.装饰(清凉);

            Console.WriteLine(水电.显示());
            Console.Read();
        }
    }
}


------解决方案--------------------
lz既然想说装饰,首先搞清楚装饰的动机:原有的类不具备某个功能,通过装饰增加上。

但是lz的例子显然是多此一举。为了更加明显地体现“装饰”,我们索性把工资的概念从员工上面剥除。
员工类完全不知道什么工资。

我们使用C#3的扩展方法给员工增加了工资计算的方法。

在内部,我们使用享元模式,从而为员工附加上对应的工资数据。

在使用扩展方法的时候,需要强调一点:目前只有C#支持扩展方法,所以其它语言,必须使用传统的方法调用,这就使得this对象可能为null,所以一定要先判断!
我们按照.NET的习惯,如果对象为null,丢出NullReferenceException。

另外,扩展方法只作用于定义的那个命名空间。这也为我们扩展不同的工资算法提供了可能。

因为扩展方法中保存了对象扩展的数据(这里的_data),所以对于持久化/序列化的时候要注意状态的保持和恢复。建议配合重写GetHashCode()实现这一点。

最后普及下。如果扩展方法和类自身的成员重名,而且方法参数相同,执行哪一个呢?记住,类的成员方法优先级更高。

最后说一下,以这个例子为例,最好的设计还是什么模式都不用。你应该将工资计算的代码直接写在员工类里面。如果你需要扩展工资算法,可以考虑使用模板方法模式。越多的类造成越复杂的系统。

尤其是你最开始的系统,如果我希望显示输出不要那个换行,你需要修改几个类的源代码?而这些源代码都是重复的。
我的异常网推荐解决方案:软件开发者薪资,http://www.myexception.cn/other/1391128.html