设计模式之策略模式

策略模式

概念

策略模式的思想是针对一组算法,将每种算法都封装到具有共同接口的独立的类中,从而使他们可以互相替换。策略模式的最大特点是使算法可以在不影响客户端的情况下发生变化,从而改变不同的功能。

特点

策略模式体现了面向对象程序设计中的非常重要的两个原则:

1.封装变化的概念。

2.编程中使用接口,而不是使用的是具体的实现类(面向接口编程)。

策略模式的应用

在正式应用策略模式之前,我们先讲一个小故事:现在小刘上班的公司做了一套相当成功的模拟鸭子游戏。游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫,此系统的内部设计使用了标准的OO技术,设计了一个鸭子的超类,并且让各种鸭子继承了此超类。去年公司的竞争压力加剧,在经过了为期一周的头脑风暴会议后,公司主管认为是该创新的时候了,他们需要展示一些让人真正印象深刻的东西来振奋人心。主管们决定此模拟程序需要会飞的鸭子来将竞争者抛在脑后。小刘的经理拍胸脯告诉主管们,小刘需要一个星期就能搞定。“毕竟,小刘是一个OO程序猿……这有什么困难?”

当需求传达给小刘的时候小刘心想:“既然所有的鸭子都继承自鸭子的超类,那么只需要在超类中加上fly()方法,那么所有的鸭子都会继承fly(),所有的鸭子都会飞了。”小刘迅速的修改了代码,果真所有的鸭子都能飞了。然而,问题出现了……在一次产品展示上,经理发现居然有很多“橡皮鸭子”在屏幕上飞来飞去。小刘在修改代码的时候忽略了一件事,不是所有的鸭子都会飞,小刘在超类中加了飞的行为会使一些不该有此行为的子类也拥有了此行为。对代码所作的局部修改,影响层面却不只是局部。他深刻体会到了一件事:当涉及“维护”时,为了“复用”而使用继承,结局并不完美。

小刘为了解决橡皮鸭子会飞的这个bug又想了新的办法:将橡皮鸭中的fly()方法覆盖掉,让其什么也不做。但是小刘又发愁:如果以后有新鸭子需要添加,他们可能有的只会叫不会飞,有的可能不会飞也不会叫,那么他都要去检查并且可能需要覆盖鸭子的行为,这简直是无穷无尽的噩梦。所以他需要一个更加清晰的方法,让某些鸭子类型可飞或者可叫。小刘心想他可以把fly()从超类中取出来做成一个接口,有鸭子会飞就实现这个接口,这样的话就实现了只有某些鸭子会飞。然而小刘的同事告诉他:“这个主意坏透了,你没发现这样重复的代码会变多吗?而且假如有50类鸭子都实现了这个接口,某天需要修改一下飞的行为,你又怎么说?”小刘听了陷入无尽的思考当中。

小刘的同事小杨看到小刘苦苦想不出答案决定帮帮他:“既然鸭子的飞和叫的行为会随着鸭子的不同而改变,为了能够把这两个行为从鸭子类中分开,为何不考虑将他们从鸭子类中取出来,建立一组新类来代表每个行为呢?飞行行为有很多,可能用翅膀飞,也可能飞不起来,也可能靠风吹着飞,完全可以将飞作为接口,上述这三种飞行方式为飞行接口的实现类,到时候只需要在鸭子类中声明接口并且赋予飞行方式就好了。”小刘听了小杨的话恍然大悟,原来这就是面向接口编程。小刘在跟小杨的讨论中确定了实现方式并且写出了如下代码:

/**
 * 飞行行为接口
 */
public interface FlyBehavior {
    void fly();
}

/**
 * 翅膀飞行实现类
 */
public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("i'm flying!");
    }
}

/**
 * 不能飞行实现类
 */
public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("i can't fly");
    }
}

/**
 * 鸭子超类
 */
public abstract class Duck {

    FlyBehavior flyBehavior;

    public void swim() {
        System.out.println("All dicks float, even decoys!");
    }

    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public abstract void display();
    public void performFly() {
        flyBehavior.fly();
    }
}

/**
 * 绿头鸭实现类
 */
public class MallardDuck extends Duck {
    public MallardDuck(){
        flyBehavior = new FlyWithWings();
    }

    @Override
    public void display() {
        System.out.println("i'm a real mallard duck");
    }
}

public class Main {

    public static void main(String[] args) {
        Duck mallardDuck = new MallardDuck();
        mallardDuck.display();
        mallardDuck.performFly();

    }
}

小刘测试后发现果真使用面向接口编程可以有效地将代码解耦,而且在程序运行中可以方便的改变鸭子的飞行方式。假如有只绿头鸭不能飞了只需要

        mallardDuck.setFlyBehavior(new FlyNoWay());

就可以将鸭子设置为不能飞的状态。

小刘经过此次经历获得了很多感悟:

在程序设计中,应当多用组合少用继承,使用组合建立系统具有很大的弹性。不仅可以将算法族封装成类,更可以在在运行时动态的改变行为,只要组合的行为对象符合正确的接口标准即可。例如鸭子模拟系统中,鸭子的行为不是继承来的,而是和适当的行为对象组合来的。当鸭子增加了新的飞行方式时,例如喷气火箭飞行只需要创建喷气火箭飞行类并且实现飞行的接口即可。不会对原有代码产生影响。

public class FlyRocketPowered implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("i'm flying with a rocket!");
    }
}

策略模式正式定义:

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值