• 策略模式


    缘起

    每周一篇的计划刚定下就迎来了第一周,那我该写些什么呢?思考了一下,刚好最近在看Head First 设计模式一书,那么我就来说说设计模式吧。虽然关于设计模式的博客,博客园里已经有很多大佬写过了,我就权当加深自己的印象和理解。

    开始

    首先,我们来提一个需求。我们拥有一款鸭子模拟游戏,现在我们有两种鸭子类型,一种绿头鸭(MallardDuck),一种红头鸭(RedheadDuck),它们都拥有外貌描述(dispaly)、叫(quack)和飞(fly)的方法。
    那么代码会是这样的

    public abstract class Duck
    {
        public abstract void Display();
        public void Quack()
        {
            Console.WriteLine("呱呱");
        }
          public void Fly()
        {
            Console.WriteLine("飞行");
        }
    }
    public class MallardDuck:Duck
    {
        public override void Display()
        {
            Console.WriteLine("我是绿头的");
        }
    }
    public class RedheadDuck:Duck
    {
        public override void Display()
        {
            Console.WriteLine("我是红头的");
        }
    }
    

    这样已经满足了我们的需求,但这时产品又提出了一个需求,我想要增加一种新的鸭子类型-橡皮鸭(RubberDuck)。这简单,我们这样做:

    public class RubberDuck:Duck
    {
        public override void Display()
        {
            Console.WriteLine("我是橡皮鸭的");
        }
    }
    

    是不是看出来哪里不对了。是的,橡皮鸭怎么会飞呢,而且它也不是“呱呱”的叫。当需求改变时,为了复用之前的代码,我们用了继承,结局并不完美。

    当涉及“维护”时,为了“复用”(reuse)目的而使用继承,结局并不完美。

    如何解决呢?也很简单。我们重写Duck的Fly方法和Quack方法,并且Fly方法什么也不干。

    public class RubberDuck:Duck
    {
        public override void Display()
        {
            Console.WriteLine("我是橡皮鸭的");
        }
        public override void Quack()
        {
            Console.WriteLine("嘎嘎");
        }
        public override void Fly()
        {
    
        }
    }
    

    可是这样就完美了吗?每当有新的鸭子类出现,我们就要被迫检查并可能需要覆盖Fly()和Quack()方法。那我们将Fly()从超类中提取出来,放进一个IFlyable接口,只有会飞的鸭子才实现此接口,这种解决方法让我们必须去实现接口的方法,代码无法复用,只是从一个噩梦跳入另一个噩梦。那么这个问题是不是无解了?

    刚好我们最近看了Head First设计模式,看看有没有适合我们使用的方式。

    找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

    Duck类仍是超类,但我们将会变化的部分,也就是Fly和Quack取出。

    接着我们设计鸭子的行为

    针对接口(超类)编程,而不是针对实现编程。

    根据设计原则,我们将这样设计

    public interface IFlyBehavior
    {
        void Fly();
    }
    public class FlyNoWay : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("不会飞");
        }
    }
    public class FlyWithWings : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("飞行");
        }
    }
    

    将Fly行为定义为接口,并让FlyNoWay类和FlyWithWings类实现它,Quack行为同理。接着修改Duck类,申明两个接口对象,添加PerformQuack方法和PerformFly方法来实现原先的Quack和Fly方法。

    public abstract class Duck
    {
        public IFlyBehavior flyBehavior;
        public IQuackBehavior quackBehavior;
    
        public void PerformQuack()
        {
            quackBehavior.Quack();
        }
    
        public void PerformFly()
        {
            flyBehavior.Fly();
        }
    
        public abstract void Display();
    }
    

    让我们来调用看看:

    class Program
    {
        static void Main(string[] args)
        {
            var mallard = new MallardDuck();
            mallard.PerformQuack();
            mallard.PerformFly();
        }
    }
    

    结果是正确的,绿头鸭会呱呱叫也会飞。我们接着优化一下,可以动态的设定行为。

    public abstract class Duck
    {
        public IFlyBehavior flyBehavior;
        public IQuackBehavior quackBehavior;
    
        public void PerformQuack()
        {
            quackBehavior.Quack();
        }
    
        public void PerformFly()
        {
            flyBehavior.Fly();
        }
    
        public abstract void Display();
    
        public void setFlyBehavior(IFlyBehavior fb)
        {
            flyBehavior = fb;
        }
    
        public void setQuackBehavior(IQuackBehavior qb)
        {
            quackBehavior = qb;
        }
    }
    

    新加setFlyBehavior和setQuackBehavior方法,让我们可以动态的传入鸭子的行为,接着我们来运行一下

    class Program
    {
        static void Main(string[] args)
        {
    
            var mallard = new MallardDuck();
            mallard.PerformQuack();
            mallard.PerformFly();
    
            mallard.setFlyBehavior(new FlyNoWay());
            mallard.setQuackBehavior(new QuackNoWay());
    
            mallard.PerformQuack();
            mallard.PerformFly();
        }
    }
    

    策略模式:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

    结语

    感觉没有对策略模式总结的很到位,第一次写技术的博客也没什么思路,基本按书本的流程走下来,废话偏多,文章也不够简洁,接下去会多多思考,多多总结,提炼出文章中的精华。

  • 相关阅读:
    web控件文本框不响应回车事件
    封装的概念
    js 中eval的使用
    C#调用存储过程
    javascript和C#对URI编码
    比较好的博客日历控件
    扩展方法实例
    C# 集合类
    数据访问层的几种数据库连接方式
    aspnetpage分页控件的使用
  • 原文地址:https://www.cnblogs.com/jiuxi/p/11973000.html
Copyright © 2020-2023  润新知