zoukankan      html  css  js  c++  java
  • 策略模式

    缘起

    每周一篇的计划刚定下就迎来了第一周,那我该写些什么呢?思考了一下,刚好最近在看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();
        }
    }
    

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

    结语

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

  • 相关阅读:
    【PAT甲级】1043 Is It a Binary Search Tree (25 分)(判断是否为BST的先序遍历并输出后序遍历)
    Educational Codeforces Round 73 (Rated for Div. 2)F(线段树,扫描线)
    【PAT甲级】1042 Shuffling Machine (20 分)
    【PAT甲级】1041 Be Unique (20 分)(多重集)
    【PAT甲级】1040 Longest Symmetric String (25 分)(cin.getline(s,1007))
    【PAT甲级】1039 Course List for Student (25 分)(vector嵌套于map,段错误原因未知)
    Codeforces Round #588 (Div. 2)E(DFS,思维,__gcd,树)
    2017-3-9 SQL server 数据库
    2017-3-8 学生信息展示习题
    2017-3-5 C#基础 函数--递归
  • 原文地址:https://www.cnblogs.com/jiuxi/p/11973000.html
Copyright © 2011-2022 走看看