设计模式(1)策略模式

先假设我们要做一个模拟鸭子的游戏,这群鸭子会在水里游泳、会嘎嘎的叫,那么运用OO的思想,先设计一个鸭子作为父类,然后所有的鸭子都继承这个父类。

现在,鸭子模拟游戏1.0版本就诞生了,这些鸭子继承了父类的游泳、嘎嘎叫,以及各自都有自己的描述。

接下来就是开发鸭子模拟游戏2.0了,现在需要给鸭子扩展一些模拟,例如飞行、还要给鸭子扩展一些种类,例如模型鸭、橡皮鸭。

这时候就可以继续扩展鸭子父类的属性,并且多添加几个子类。

由于模型鸭、橡皮鸭不会叫也不会飞,所以在子类中重载了quack()方法和fly()方法。

但是这个设计也是不合理的,因为每次有新的鸭子出现,就需要对quack()和fly()进行检查,判断这个类型的鸭子需不需要覆盖这两个方法,需要有一种更清晰的方法。

于是鸭子模拟游戏2.1版本就这样诞生了,这个时候将fly()和quack()从父类中提取出来,放入到两个接口中去,只有会飞、会嘎嘎叫的鸭子才会去继承这两个接口。

但是这样设计出来的结构,也是有缺陷的,例如Mallard和RedheadDuck它们的叫声都是“嘎嘎”,但是却写了重复的quack()代码,没有实现代码的复用。

我们需要重新考虑下这个鸭子模拟游戏整体的设计了,首先鸭子的fly()和quack()行为会频繁的根据鸭子的种类进行变化,基于设计原则——找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码放在一起。我们可以把鸭子的fly()和quack()行为独立出来,从Duck类中提出,建立一组新类代表这些行为。

鸭子fly()的方式会有多种,如有的鸭子是普通的飞行、有的鸭子是鸭子中的战斗鸭,它们的飞行就是特技飞行了。但是无论它们以何种方式进行飞行,它们都是属于飞行这一个行为。再考虑到鸭子会进行锻炼,从一只普通的鸭子变成了战斗鸭,那它的飞行模式也进行了升级变成了特技飞行,这时就需要能够动态的改变鸭子的飞行行为,基于设计原则——针对接口编程,而不是针对实现编程。我们可以利用接口代表一个行为,如FlyBehavior和QuackBehavior,由fly行为类和quack行为类去具体实现。

剩下的工作就是把这些行为整合到Duck类中了,可以在Duck定义两个变量,类型为FlyBehavior和QuackBehavior,每个鸭子对象都可以动态的设置这些动作运行时具体的行为类型。

于是鸭子模拟游戏3.0诞生了

/**
 * 鸭子飞行为
 *
 * @author ousy
 * @since 2019-01-27 23:21:23
 */
public interface FlyBehavior {
    /**
     * 鸭子飞行为
     */
    public void fly();
}
FlyBehavior
/**
 * 鸭子叫行为
 *
 * @author ousy
 * @since 2019-01-27 23:21:38
 */
public interface QuackBehavior {
    /**
     * 鸭子叫行为
     */
    public void quack();
}
QuackBehavior
/**
 * 鸭子普通飞行为
 *
 * @author ousy
 * @since 2019-01-27 23:28:33
 */
public class NormalFly implements FlyBehavior {
    /**
     * 鸭子飞行为
     */
    @Override
    public void fly() {
        System.out.println("普通的飞");
    }
}
NormalFly
/**
 * 鸭子特技飞行为
 *
 * @author ousy
 * @since 2019-01-27 23:28:41
 */
public class SpecialFly implements FlyBehavior {
    /**
     * 鸭子飞行为
     */
    @Override
    public void fly() {
        System.out.println("耍特技的飞");
    }
}
SpecialFly
/**
 * 鸭子普通的叫
 *
 * @author ousy
 * @since 2019-01-27 23:28:47
 */
public class NormalQuack implements QuackBehavior {
    /**
     * 鸭子叫行为
     */
    @Override
    public void quack() {
        System.out.println("鸭子普通的叫");
    }
}
NormalQuack
/**
 * 鸭子特技叫行为
 *
 * @author ousy
 * @since 2019-01-27 23:29:01
 */
public class SpecialQuack implements QuackBehavior {
    /**
     * 鸭子叫行为
     */
    @Override
    public void quack() {
        System.out.println("鸭子特技的叫");
    }
}
SpecialQuack
/**
 * 鸭子类
 *
 * @author ousy
 * @since 2019-01-27 23:21:57
 */
public class Duck {
    /**
     * 定义fly行为
     */
    private FlyBehavior flyBehavior;
    /**
     * 定义quack行为
     */
    private QuackBehavior quackBehavior;

    /**
     * 游泳
     */
    public void swim() {
        System.out.println("鸭子在水里游泳");
    }

    /**
     * 描述
     */
    public void display() {

    }

    /**
     * 鸭子飞
     */
    public void fly() {
        flyBehavior.fly();
    }

    /**
     * 鸭子叫
     */
    public void quack() {
        quackBehavior.quack();
    }

    /**
     * 改变飞行行为
     *
     * @param flyBehavior 飞行行为
     */
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    /**
     * 改版叫行为
     *
     * @param quackBehavior 叫行为
     */
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}
Duck
/**
 * 普通的鸭子
 *
 * @author ousy
 * @since 2019-01-27 23:32:01
 */
public class NormalDuck extends Duck {
    /**
     * 鸭子描述
     */
    @Override
    public void display() {
        System.out.println("一只普通的鸭子");
    }
}
NormalDuck
public static void main(String[] args) {
        Duck duck = new NormalDuck();
        duck.display();
        duck.setFlyBehavior(new NormalFly());
        duck.setQuackBehavior(new NormalQuack());
        duck.fly();
        duck.quack();
        System.out.println("普通的鸭子经过了锻炼变成了一只战斗鸭");
        duck.setFlyBehavior(new SpecialFly());
        duck.setQuackBehavior(new SpecialQuack());
        duck.fly();
        duck.quack();
    }
执行代码

输出


一只普通的鸭子
普通的飞
鸭子普通的叫
普通的鸭子经过了锻炼变成了一只战斗鸭
耍特技的飞
鸭子特技的叫


 

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

这种模式可以用于设计使用多种数据库的组件,例如程序需要根据情况去访问不同的数据库,MySQL、Oracle、SQLServer,可以将各个数据库的连接对象继承一个接口,然后根据具体情况去使用不同的连接。

策略模式可以很方便的添加新的策略,如鸭子除了NormalQuack、SpecialQuack,它还会WhisperyQuack(轻声的叫),这样就只要创建一个WhisperyQuack类继承QuackBehavior就可以实现了。

但是它也有缺点,策略的增多不可避免的会导致类越来越多,而且策略之间的算法是平级的,他们不能实现相互的调用。

posted @ 2019-01-27 23:56  oeleven  阅读(245)  评论(0编辑  收藏  举报