zoukankan      html  css  js  c++  java
  • 第一章:策略模式

    我们要创建一个鸭子世界,这里的鸭子会飞还会叫(嘎嘎嘎)。当然鸭子的种类也有很多,红头的、绿头的等等。

    第一个设计方案:继承

    我是OO程序员,我在基类中实现fly、quack和display方法。子类继承基类的方法,这样代码还可以得到复用。看起来真是个好主意。

    image

     

    public class Duck {
        public void quack() {
            System.out.println("鸭子嘎嘎叫");
        }
        
        public void display() {
            System.out.println("一只鸭子");
        }
        
        public void fly() {
            System.out.println("鸭子在飞");
        }
    }
    
    public class RedheadDuck extends Duck {
        @Override
        public void display() {
            System.out.println("一直红头鸭子");
        }
    }

    糟糕:橡胶鸭来到了鸭子世界

    image

    橡胶鸭也是鸭子,那很自然地继承鸭子类。

    image

    结果你会发现橡胶鸭也会飞?这显然是不合适的,并不是所有鸭子都会飞。你是个OO程序员,你应该想到了override,让我们试试:

    //橡胶鸭继承自Duck,橡胶鸭不会飞但是会叫
    public class RubberDuck extends Duck {
        @Override
        public void display() {
            System.out.println("一直橡胶鸭子");
        }
        
        @Override
        public void fly() {
            //什么都不做
        }
    }

    第二个设计方案:用接口

    image

    看起来是个不错的设计。

    绿头鸭会飞会叫所以实现了这两个接口。

    橡胶鸭只会叫,所以实现了“叫”这个接口。

    木制鸭既不会飞又不会叫,所以一个接口都没有实现。

    代码复用率也太低了吧

    我们看下Java代码,观察一下quack()方法重复了好多次。要是以后修改了quack()方法,想想你是要改多少个地方。。。

    //红头鸭
    public class RedheadDuck extends Duck implements Flyable, Quackable {
        @Override
        public void display() {
            System.out.println("一直红头鸭子");
        }
    
        @Override
        public void quack() {
            System.out.println("嘎嘎叫");
        }
    
        @Override
        public void fly() {
            System.out.println("飞啊飞");
        }
    }
    
    //橡胶鸭
    public class RubberDuck extends Duck implements Quackable{
        @Override
        public void display() {
            System.out.println("一直橡胶鸭子");
        }
    
        @Override
        public void quack() {
            System.out.println("嘎嘎叫");
        }
    }

    第三个设计方案:策略模式

    image

    这个设计一眼看去很奇怪,我们把飞和叫这种行为也定义成类了。类不应该是对现实实体的抽象吗?飞是一种动作,把它定义成类总觉得奇怪。

    看这个UML图的时候请注意以下几点:

    • 基类(鸭子类)是面向接口的,它的两个字段(flyBehavior和quackBehavior)的类型都是接口。
    • 鸭子可能有很多种飞行的方式,“用翅膀”或者“坐火箭”。未来可能会增加其它方式,比如:用竹蜻蜓飞。策略模式可以很好地应对这种扩展。
    • 以后可能要修改"用翅膀飞"这个方法,那么我们只需要修改一处代码就可以。

    光看这个类图你一定还是不理解策略模式到底在做什么,在下面列出的Java代码中请注意构造函数的实现。

    public class RedHeadDuck extends Duck {
        public RedHeadDuck() {
            flyBehavior = new FlyWithWing();
            quackBehavior = new Quack();
        }
        
        @Override
        public void display() {
            System.out.println("一直红头鸭子");
        }
    }
    
    public class RubberDuck extends Duck {
        public RubberDuck() {
            //橡胶鸭不会飞
            flyBehavior = null;
            //叫起来想猫一样(似乎是一家奇葩的厂商生产的橡胶鸭)
            quackBehavior = new QuackLikeCat();
        }
        
        @Override
        public void display() {
            System.out.println("一只橡胶鸭");
        }
    }
    
    public class Duck {
        protected FlyBehavior flyBehavior;
        protected QuackBehavior quackBehavior;
        
        public void display() {
        }
        
        public void performQuack() {
            if (flyBehavior != null) {
                flyBehavior.fly();
            }
        }
        
        public void performFly() {
            if (quackBehavior != null) {
                quackBehavior.quack();
            }
        }
    }
    
    public interface FlyBehavior {
        public void fly();
    }
    
    public class FlyWithWing implements FlyBehavior {
        @Override
        public void fly() {
            System.out.println("两只翅膀,啪嗒啪嗒飞");
        }
    }
    
    public class FlyWithRocket implements FlyBehavior {
        @Override
        public void fly() {
            System.out.println("乘坐火箭,嗖一下飞过去了");
        }
    }
    
    public interface QuackBehavior {
        public void quack();
    }
    
    public class QuackLikeCat implements QuackBehavior {
        @Override
        public void quack() {
            System.out.println("喵喵叫");
        }
    }
    
    public class Quack implements QuackBehavior{
        @Override
        public void quack() {
            System.out.println("嘎嘎叫");
        }
    }

    练习

    如果你认为已经看懂了策略模式,那么试试下面的练习,会帮助你更好理解。

    • 把Duck改成抽象类,并将display改成抽象方法。
      创造一个会坐火箭飞的鸭子。
    • 把FlyWithWing类的fly方法进行更改(改成什么随便你),留心代码复用的问题。
    • 增加一个使用竹蜻蜓飞行的行为,看看是不是很容易拓展呀。
    • 我们现在必须在构造函数中指定Duck的Fly和Quack行为。现在让我们在Duck中增加set方法,使之能动态改变Fly行为和Quack行为。
  • 相关阅读:
    29-赫夫曼树
    28-线索化二叉树
    27-顺序存储二叉树
    26-二叉树的遍历查找和删除
    25-二叉树的概念
    24-逻辑结构分析
    23-哈希表
    22-查找算法
    21-堆排序
    Mui-列表/table-view
  • 原文地址:https://www.cnblogs.com/dongchen/p/5011027.html
Copyright © 2011-2022 走看看