(9)结构型模式——装饰器

结构型模式——装饰器(Decorator)

问题背景

当需要动态地扩展一个对象时,可以考虑使用装饰器。用RPG游戏中烂大街的宝石镶嵌功能举例:现有一个装备类,在设计之初并没有考虑镶嵌功能,如果要在类内部加入镶嵌逻辑,就必须修改原有代码。而修改原有代码很容易引起回归错误,尤其是进行这种大规模改动的时候。

解决方案

由于设计之初没有考虑需求变化,从内部扩展就成了不可能,于是我们考虑在外部扩展。假设待扩展类实现了接口IEquipment,设计一个装饰器类GemEquipment表示镶嵌了宝石的装备,令它也实现IEquipment,并持有一个IEquipment的引用,在使用装饰器时,增加一些处理。这样,原对象和装饰器对用户就是透明的,用户在使用装饰器时的感觉就好像是原对象本身获得了增强一样。使用装饰器后的程序结构是这样的:
程序结构
值得一提的是,由于GemEquipment也实现接口IEquipment,所以同样可以引用装饰器类。这样,就形成了递归嵌套,通过这种结构,可以无限增强一个对象。同时,这种扩展是针对对象的扩展,而非针对类的扩展,因此具有动态性,支持热插拔,非常灵活。

效果

  1. 面向接口编程,提高了可扩展性。
  2. 使用组合,提高了动态性。
  3. 保证了原对象的静态结构不会过于臃肿,易于维护。

缺陷

装饰器的结构是一个栈而非表,因此必须按照LIFO规则进行插拔,这在一定程度上限制了程序的灵活性。装饰器提供了一定的透明性,但不如复合来的强。装饰器和原对象是有区别的,而复合结构的每个节点都是相同的。如果侧重点是透明性,请用复合。过多的装饰器会造成小对象过多,调用链过长的问题,影响程序性能。

相关模式

  1. 适配器:适配器和装饰器易混淆,适配器的作用是转换接口,装饰器的作用是动态增强。识别方式就是看目标对象的接口是否发生了变化。
  2. 复合:装饰器是一个特殊的复合结构,但侧重点是增强。因此,不应该把装饰器当复合用,但可以用装饰器来增强复合结构中的结点。
  3. 策略:装饰器是在对象外部进行增强,而策略是在对象内部进行增强;装饰器一般用于设计时未考虑的变化,策略一般用于设计时考虑到的变化。

实现

using System;

namespace Decorator
{
    class Client
    {
        public interface IEquipment
        {
            int ATK { get; }
            int DEF { get; }
            void Show();
        }

        public class Equipment : IEquipment
        {
            public int ATK { get; }
            public int DEF { get; }
            public Equipment(int atk, int def)
            {
                ATK = atk;
                DEF = def;
            }
            public void Show()
            {
                Console.WriteLine($"攻击力: {ATK}, 防御力: {DEF}");
            }
        }

        public class GemEquipment : IEquipment
        {
            private IEquipment target;
            private Gem gem;
            private int atk;
            private int def;
            public GemEquipment(IEquipment target, Gem gem)
            {
                this.target = target;
                this.gem = gem;
            }
            public int ATK
            {
                get
                {
                    return gem.ATK + target.ATK;
                }
            }
            public int DEF
            {
                get
                {
                    return gem.DEF + target.DEF;
                }
            }
            public void Show()
            {
                Console.WriteLine($"攻击力: {ATK}, 防御力: {DEF}");
            }
        }

        public class Gem
        {
            public Gem(int atk, int def)
            {
                ATK = atk;
                DEF = def;
            }
            public int ATK { get; }
            public int DEF { get; }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("创建装备...");
            IEquipment equipment = new Equipment(100, 50);
            equipment.Show();

            Console.WriteLine("镶嵌宝石...");
            equipment = new GemEquipment(equipment, new Gem(25, 0));
            equipment.Show();

            Console.WriteLine("再来一颗...");
            equipment = new GemEquipment(equipment, new Gem(10, 10));
            equipment.Show();
        }
    }
}

运行结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值