首页 > 技术文章 > 装饰模式

ningxinjie 2020-01-19 14:06 原文

装饰模式适合的最恰当的形容词就是:子类复子类,子类何其多。 如果一个基类有多个子类,而各个子类可能具有不同的功能,同一个子类也可能具有不同的功能,如果我们将一个功能定义为实现一个接口,那么同一个子类要实现不同的功能,那么他要在子类的基础上继续创建子类,无穷无尽。。。

 

例子使用:坦克分为T50与T75,坦克具有基本的发射与移动功能,其中具有附加功能(红外功能、水陆两栖功能、卫星定位功能)

如果我们不使用装饰模式的话,那么我们实现的代码:

#region 抽象坦克基类
    public abstract class Tank
    {
        public abstract void Shot();
        public abstract void Run();
    }
    #endregion
  #region 具体坦克类
    public class Tank50 : Tank
    {
        public override void Run()
        {
            Console.WriteLine(this.GetType().Name+"在奔跑");
        }

        public override void Shot()
        {
            Console.WriteLine(this.GetType().Name + "在发射炮弹");
        }
    }
    public class Tank75 : Tank
    {
        public override void Run()
        {
            Console.WriteLine(this.GetType().Name + "在奔跑");
        }

        public override void Shot()
        {
            Console.WriteLine(this.GetType().Name + "在发射炮弹");
        }
    }
    #endregion
  #region 功能接口
    interface IRedLine
    {
        void RedLine();
    }
    interface IWaterPath
    {
        void WaterPath();
    }
    interface IPosition
    {
        void IPosition();
    }
    #endregion
  #region 功能坦克类
    public class Tank50A : Tank50, IRedLine
    {
        public void RedLine()
        {
            Console.WriteLine("具有红外功能");
        }
    }
    public class Tank50B : Tank50, IRedLine,IWaterPath
    {
        public void RedLine()
        {
            Console.WriteLine("具有红外功能");
        }

        public void WaterPath()
        {
            Console.WriteLine("具有水陆两栖功能");
        }
    }
    //无限扩展
    //......
    #endregion

 

装饰模式避免了这种问题,它的设计思路不是将功能化为接口,而是将每个功能实现在每个装饰类中。

坦克类仍然不变,如下:

   #region 抽象坦克基类
    public abstract class Tank
    {
        public abstract void Shot();
        public abstract void Run();
    }
    #endregion
 #region 具体坦克类
    public class Tank50 : Tank
    {
        public override void Run()
        {
            Console.WriteLine(this.GetType().Name+"在奔跑");
        }

        public override void Shot()
        {
            Console.WriteLine(this.GetType().Name + "在发射炮弹");
        }
    }
    public class Tank75 : Tank
    {
        public override void Run()
        {
            Console.WriteLine(this.GetType().Name + "在奔跑");
        }

        public override void Shot()
        {
            Console.WriteLine(this.GetType().Name + "在发射炮弹");
        }
    }
    #endregion

 

装饰类如下:

  #region 装饰基类
    public abstract class Decorator :Tank
    {
        private Tank _tank;
        public Decorator(Tank tank)
        {
            _tank = tank;
        }
        public override void Run()
        {
            _tank.Run();
        }
        public override void Shot()
        {
            _tank.Shot();
        }
    }
    #endregion

 

  #region 具体功能装饰类
    public class DecoratorRedLine : Decorator
    {
        public DecoratorRedLine(Tank tank) : base(tank)
        {

        }
        public override void Run()
        {
            base.Run();
        }
        public override void Shot()
        {
            Console.WriteLine("具有红外功能");
            base.Shot();//这里就是重点!!!!可以调用构造函数实例时的基成员方法
        }
    }
    public class DecoratorWaterPath : Decorator
    {
        public DecoratorWaterPath(Tank tank) : base(tank)
        {

        }
        public override void Run()
        {
            base.Run();
        }
        public override void Shot()
        {
            Console.WriteLine("具有水陆两栖功能");
            base.Shot();
        }
    }
    public class DecoratorPosition : Decorator
    {
        public DecoratorPosition(Tank tank) : base(tank)
        {

        }
        public override void Run()
        {
            base.Run();
        }
        public override void Shot()
        {
            Console.WriteLine("具有定位功能");
            base.Shot();
        }
    }
    #endregion
 static void Main(string[] args)
        {
            Tank tank = new Tank50();//实例化一个坦克类
            Decorator decorator = new DecoratorRedLine(tank);
            Decorator decorator2 = new DecoratorWaterPath(decorator);//这个shot时会调用decorator的shot,从而实现不断叠加
            decorator2.Shot();
            Console.ReadKey();
        }

装饰类,将各个需要随时变化的功能写在装饰的子类中,当需要使用的时候只需要调用相应的装饰子类,重点就是调用base.shot()  base的方法 ,因此在装饰的抽象类中,必须调用对需要装饰类的方法的复写,这样才能实现base的方法! 

 

设计模式的思想真的是非常好,多学,多做,多练,加油!

推荐阅读