首页 > 代码库 > 设计模式(一)The Strategy Pattern 策略模式
设计模式(一)The Strategy Pattern 策略模式
我们先来看看问题 —— 现在我们需要实现一个模拟鸭子的游戏,游戏中会出现各种各样的鸭子,他们会有不同的飞行方式,同样有不同的鸣叫方式,同时我们要考虑到以后还可能出现更多的各种各样新式的鸭子,那我们该如何来实现呢?
1>我们来试试继承
这是我们的Duck类
1 2 3 4 5 6 7 8 9 | package my.oschina.net.design.strategy; public class Duck { public void fly(){} public void quack(){} } |
现在我们可以通过继承很简单的来完成各种鸭子的实现,我们所要做的就是简单的继承Duck类,实现符合各自特征的fly()方法和quack()方法,像这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | class Rubber extends Duck { public void fly(){ System.out.println( "I can‘t fly!" ); } public void quack(){ System.out.println( "Quack" ); } } class ModelDuck extends Duck { public void fly(){ System.out.println( "I can fly with rocket!" ); } public void quack(){ System.out.println( "<<silience>>" ); } } class XX extends Duck{ public void fly(){ //XX的飞行方法} public void quack( //XX的鸣叫方法){} } class YY extends Duck{ public void fly(){ //YY的飞行方法} public void quack(){ //YY的鸣叫方法} } class ZZ extends Duck{ public void fly(){ //ZZ的飞行方法} public void quack(){ //ZZ的鸣叫方法} } . . . |
大家看到问题之所在了吗?每当我们增加一个新的子类(软件的可扩展性!软件升级的过程中这些可是必不可少的哦!),我们必须要被迫检查每一个可能需要覆盖的方法(尽管有的鸭子根本就不会飞,也根本就不会叫,这真是很头疼)。。。你确定这样你可以忍受?不管你能不能,反正我不能!
2>要不我们试试接口吧!
我们定义两个接口——flyable接口和quackable接口,这样会飞的会叫的鸭子可以实现这两个接口,不会的不需要实现这两个接口。看起来不错哦!真的不错吗?不要被忽悠!NO!错,很错!有没有发现这和继承都有一个通病——代码根本是无法复用的存在!一旦代码无法复用就意味着我们要做大量重复无意义的ctrl+c,ctrl+v工作。
那我们到底该肿么做呢?
来,我们先来看看我们在写代码的时候需要尽力遵循的两个设计原则:
a) 原则一:找出代码中可能需要变化的代码,把他们独立出来,不要和不变化的或者变化极小的代码混在一起。
b) 原则二:针对接口编程,不要针对实现编程。
关于上述两个原则的解释:
a)一旦我们将变化的和不变化的代码分割开来,以后我们可以轻易的改动或者扩展这部分的代码而不用担心其他不需要变化的代码会受到影响。
b)上面的两种方法中我们都是在针对实现来编程,一旦我们将代码写下了我们就很难来改变这些行为,我们被实现绑定的死死的,这就导致当我们要修改这些行为的时候我们不得不来这个类中大量修改这些源代码!这对于后期软件的维护工作简直不能忍!(ps.利用多态的原理,我们针对超类型来编程,执行的时候代码会自己选择适当的行为!)
下面我们再看第三个设计原则
c)多用组合少用继承,这样我们可以在运行时动态改变类的各种行为,这听起来真的很不错哦!
3>使用策略模式
好吧!入正题吧!废话多了点。现在我们来看看使用策略模式的实现方法。
策略模式——定义算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的改变独立于使用算法的客户。
一切看代码:
这是Duck类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package my.oschina.net.design.strategy; /** * 我们不变的或者是变动可能性不大的方法写在父类中(display) * 把以后可能会变化的方法抽取出来例如这里的fly与quack * * @author Eswin * */ public class Duck { //声明两个“接口”类型的变量用于动态的调用实现了这个接口的类的方法,原则二 FlyBehavior flyBehavior; QuackBehavior quackBehavior; /** * 鸭子外观 * 这个是不会变的就写在父类中,由子类来实现 */ public void display() { } /** * 动态的改变运行时特定行为的引用fly * @param flyBehavior * @return void */ public void setFly(FlyBehavior flyBehavior) { this .flyBehavior = flyBehavior; } /** * 动态的改变运行时特定行为的引用quack * @param quackBehavior * @return void */ public void setQuack(QuackBehavior quackBehavior) { this .quackBehavior = quackBehavior; } public void performFly() { //Duck并不亲自处理fly的行为而是会引用实现了FlyBehavior接口的对象 flyBehavior.fly(); } public void performQuack() { //Duck并不亲自处理fly的行为而是会引用实现了quackBehavior接口的对象 quackBehavior.quack(); } } |
两个接口FlyBehavior,QuackBehavior
1 2 3 4 5 6 7 8 9 | package my.oschina.net.design.strategy; //行为接口fly public interface FlyBehavior { public void fly(); } public interface QuackBehavior { public void quack(); } |
实现这两个接口
FlyBehavior实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package my.oschina.net.design.strategy; /** * @author Eswin */ public class FlyNoWay implements FlyBehavior { @Override public void fly() { // TODO Auto-generated method stub System.out.println( "I have No way to fly" ); } } public class FlyWithWings implements FlyBehavior{ @Override public void fly() { // TODO Auto-generated method stub System.out.println( "I can fly with wing!" ); } } |
QuackBehavior实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package my.oschina.net.design.strategy; /** * @author Eswin */ public class Squeak implements QuackBehavior{ @Override public void quack() { // TODO Auto-generated method stub System.out.println( "Squeak" ); } } public class MuteQuack implements QuackBehavior { @Override public void quack() { // TODO Auto-generated method stub System.out.println( "I can not quack" ); } } |
好了,现在我们来生成一个子类试试效果吧!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package my.oschina.net.design.strategy; public class ModelDuck extends Duck{ public ModelDuck() { //模型鸭现在不会飞 flyBehavior = new FlyNoWay(); quackBehavior = new Quack(); } public void display() { System.out.println( "I am a model duck!" ); } } |
来Test一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package my.oschina.net.design.strategy; public class TestModelDuck { public static void main(String[] args) { // TODO Auto-generated method stub Duck ModelDuck = new ModelDuck(); //默认的ModelDuck的fly与quack System.out.println( "现在的模型鸭还不可以飞" ); ModelDuck.performFly(); ModelDuck.performQuack(); //现在我们来改变ModelDuck的Fly方式,注意我们可是在动态的改变哦!原则二、三 ModelDuck.setFly( new FlyWithWings()); System.out.println( "现在的模型鸭可以飞了" ); ModelDuck.performFly(); ModelDuck.performQuack(); } } |
看看结果吧!O了!
联想 大家都玩过网游吧!我们在升级打怪买装备的时候,你的各项属性是否发生了变化?你换武器的时候算不算是动态的改变了自己的攻击行为?同样的攻击,我们可是造成了不同的伤害哦,对吧?
适用情况 1)系统中许多相关的类仅仅是行为有异;
2)系统需要能够在几种算法中快速动态的切换;
3)类中定义了多种行为 , 并且这些行为在这个类的操作中以多个条件选择语句的形式出现。
下面,我们分析Android中的动画是如何使用策略模式的。
1. 意图
定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。
策略模式使得算法可独立于使用它的客户而变化。
2. 结构图和代码
Animation不同动画的实现,主要是依靠Interpolator的不同实现而变。
设计模式(一)The Strategy Pattern 策略模式