装饰模式

Posted 青墨淡潋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰模式相关的知识,希望对你有一定的参考价值。

  学习新模式之前先复习一下旧模式:策略+工厂模式的混合实现。

    /// <summary>
    /// 这里还是采用了抽象类,而不是接口
    /// 之前尝试使用接口,但接口在类中并不能当做委托被动态改变
    /// 从这里也可以看出接口的用处:作为一种行为,有这种行为的对象都有各自的固定实现,而不会动态改变。
    /// 抽象类跟委托方法是可以动态改变的,方便用来设计策略模式
    /// 抽象方法必须包含在抽象类中
    /// </summary>
    public abstract class IAction_Attack     
    {
        public abstract void Attack();
    }

    public class PL_8 : IAction_Attack
    {
        public override void Attack()
        {
            Console.WriteLine("PL-8 Attacks.");
        }
    }

    public class PL_10 : IAction_Attack
    {
        public override void Attack()
        {
            Console.WriteLine("PL-10 Attacks.");
        }
    }

    public class CJ_10 : IAction_Attack
    {
        public override void Attack()
        {
            Console.WriteLine("CJ-10 Attacks.");
        }
    }

    public class FightPlane : IAction_Attack
    {
        public IAction_Attack CurrentWeapon { get; set; }

        /// <summary>
        /// 自身内部采用一个工厂模式生成武器策略;
        /// </summary>
        /// <param name="command"></param>
        public void ReadyToFire(string command)
        {
            switch(command)
            {
                case "PL-8":
                    CurrentWeapon = new PL_8();
                    break;

                case "PL-10":
                    CurrentWeapon = new PL_10();
                    break;

                case "CJ-10":
                    CurrentWeapon = new CJ_10();
                    break;
            }
        }

        public override void Attack()
        {
            if (CurrentWeapon == null)
                Console.WriteLine("No weapon is ready.");
            else
                CurrentWeapon.Attack();
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            var J_20 = new FightPlane();
            J_20.Attack();

            J_20.ReadyToFire("PL-8");
            J_20.Attack();

            J_20.ReadyToFire("PL-10");
            J_20.Attack();

            J_20.ReadyToFire("CJ-10");
            J_20.Attack();

            Console.Read();
        }      
    }

  由于C#没有多重继承,那么继承关系的抽象应该更加谨慎,而不应该将“攻击行为”作为“战斗机”的基类。

  战斗机与导弹之间应该是一种依赖关系,修改:

    public abstract class Missile     
    {
        public abstract void Attack();
    }

    public class PL_8 : Missile
    {
        public override void Attack()
        {
            Console.WriteLine("PL-8 Attacks.");
        }
    }

    public class PL_10 : Missile
    {
        public override void Attack()
        {
            Console.WriteLine("PL-10 Attacks.");
        }
    }

    public class CJ_10 : Missile
    {
        public override void Attack()
        {
            Console.WriteLine("CJ-10 Attacks.");
        }
    }

    public class FightPlane
    {
        public Missile CurrentWeapon { get; set; }

        /// <summary>
        /// 自身内部采用一个工厂模式生成武器策略;
        /// </summary>
        /// <param name="command"></param>
        public void ReadyToFire(string command)
        {
            switch(command)
            {
                case "PL-8":
                    CurrentWeapon = new PL_8();
                    break;

                case "PL-10":
                    CurrentWeapon = new PL_10();
                    break;

                case "CJ-10":
                    CurrentWeapon = new CJ_10();
                    break;
            }
        }

        public void Attack()
        {
            if (CurrentWeapon == null)
                Console.WriteLine("No weapon is ready.");
            else
                CurrentWeapon.Attack();
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            var J_20 = new FightPlane();
            J_20.Attack();

            J_20.ReadyToFire("PL-8");
            J_20.Attack();

            J_20.ReadyToFire("PL-10");
            J_20.Attack();

            J_20.ReadyToFire("CJ-10");
            J_20.Attack();

            Console.Read();
        }      
    }

  以上都是工厂、策略模式的复习,在这个例子上做装饰模式的学习。

 

示例:

  

    public abstract class Missile
    {
        public abstract void Attack();
    }

    public class PL_8 : Missile
    {
        public override void Attack()
        {
            Console.WriteLine("PL-8 Attacks.");
        }
    }

    public class PL_10 : Missile
    {
        public override void Attack()
        {
            Console.WriteLine("PL-10 Attacks.");
        }
    }

    public class CJ_10 : Missile
    {
        public override void Attack()
        {
            Console.WriteLine("CJ-10 Attacks.");
        }
    }

    public class FightPlane
    {
        public Missile CurrentWeapon { get; set; }

        /// <summary>
        /// 自身内部采用一个工厂模式生成武器策略;
        /// </summary>
        /// <param name="command"></param>
        public void ReadyToFire(string command)
        {
            switch (command)
            {
                case "PL-8":
                    CurrentWeapon = new PL_8();
                    break;

                case "PL-10":
                    CurrentWeapon = new PL_10();
                    break;

                case "CJ-10":
                    CurrentWeapon = new CJ_10();
                    break;
            }
        }

        public void Attack()
        {
            if (CurrentWeapon == null)
                Console.WriteLine("No weapon is ready.");
            else
                CurrentWeapon.Attack();
        }

        public virtual void Decorate()
        {
            Console.WriteLine("PLAF standard decoration.");    
        }
    }

    /// <summary>
    /// 装饰者:与被装饰者的基类相同,都表示对同一类事物进行装饰
    /// </summary>
    public class DecoratedFighter : FightPlane
    {
        /// <summary>
        /// 被装饰者
        /// 装饰模式的核心:通过动态绑定不同的被装饰者来加载上一层装饰者
        /// </summary>
        private FightPlane _fighter;
        public FightPlane Fighter
        {
            get
            {
                return _fighter;
            }
            set
            {
                _fighter = value;
            }
        }

        /// <summary>
        /// 装饰功能
        /// </summary>
        public override void Decorate()
        {
            if (Fighter != null)
                Fighter.Decorate();                
        }
    }

    public class LowVisibleFighter : DecoratedFighter
    {
        public bool IsVisible { get; set; }

        public override void Decorate()
        {
            IsVisible = true;
            Console.WriteLine("Low visible decoration.");
            base.Decorate();
        }
    }

    public class BlackSilkFighter : DecoratedFighter
    {
        public override void Decorate()
        {
            Console.WriteLine("Black silk decoration.");
            base.Decorate();
        }
    }

    public class StealthFighter : DecoratedFighter
    {
        public bool IsStealth { get; set; }

        public override void Decorate()
        {
            Console.WriteLine("Stealth paint decoration.");
            base.Decorate();
        }

        public bool BeingDetect()
        {
            if (IsStealth == true)
                return false;
            else
                return true;
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            var Standard_J20 = new FightPlane();

            var LowVisible_J20 = new LowVisibleFighter();
            LowVisible_J20.Fighter = Standard_J20;

            var Stealth_J20 = new StealthFighter();
            Stealth_J20.Fighter = LowVisible_J20;
            Stealth_J20.Decorate();

            Stealth_J20.ReadyToFire("PL-8");
            Stealth_J20.Attack();

            Stealth_J20.ReadyToFire("PL-10");
            Stealth_J20.Attack();

            Stealth_J20.ReadyToFire("CJ-10");
            Stealth_J20.Attack();

            Console.Read();
        }
    }

UML类图:

技术分享

类图小结:

  1. 整个结构以 FightPlane 为基类,表示为同一类对象,始终保持继承 Decorate 这个行为;

  2. 为了灵活的装饰顺序,装饰子类统一继承装饰基类,而不固定继承顺序,装饰基类 DecoratedFighter 额外聚合了一个基类来记录上一层的装饰类;

  3. 每一层的 Decorate 除了执行本层装饰之外,还应调用 记录了上一层装饰的类先执行上一层装饰;

 

  如果只需要固定装饰顺序,完全只需要用单向的继承来做就行。

以上是关于装饰模式的主要内容,如果未能解决你的问题,请参考以下文章

Thymeleaf 模板 - 有没有办法装饰模板而不是包含模板片段?

swift设计模式学习 - 装饰模式代码大全

设计模式---装饰者模式

设计模式之单例模式

装饰模式与代理模式的区别

Java设计模式之装饰者模式