设计模式–策略模式
Posted Mario_oo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式–策略模式相关的知识,希望对你有一定的参考价值。
设计模式–策略模式
@(设计模式)[策略模式|读书笔记|Head First设计模式]
策略模式:该模式定义了算法族,让算法和对象分开,使得算法可以独立于使用它的对象而变化。
例子
设计一套成功的模拟鸭子游戏:游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。
如上图所示,我们定义一个Duck父类,实现其中quack()和swim()方法,并定义一个抽象的方法display(); 具体的子类继承父类Duck的quack()和swim()方法,并实现其特定的display()方法。
新需求:现在我们需要根据鸭子的不同加上会飞的功能。
如上图所示,在父类Duck中增加一个新方法,会使得某些并不适合该行为的子类也具有该行为。( 对代码做局部修改,影响的层面不只是局部。(如例子中会导致会飞的橡皮鸭))所以我们在其特定的子类中必须覆盖重写其方法,来体现出该鸭子的特殊性。(橡皮鸭。)
如果又来一种新种类的鸭子(诱饵鸭–既不会飞也不会叫)我们又要在其子类中重写其各种方法。
利用继承的方式来提供Duck的行为,会导致:
1.代码在多个子类中重复。
2.在父类中的改变会牵一发而动全身,造成其他子类的鸭子不想要的改变。
3.运行时的行为不容易改变。
4.很难知道所有鸭子的全部行为。
这时大家可能会想到利用接口来辅助
把fly()和quack()从父类中抽取出来,定义在接口中,每个具体的子类来实现其接口方法。
虽然利用接口可以解决问题。但是这么一来重复的代码会变得更多,并且代码无法复用。
总结问题
我们已经知道使用继承并不能很好地解决问题,鸭子的行为在子类是不断变化的,如果修改父类的方法(给鸭子增加一个新的行为)就会导致所有的子类都具有该行为,必须在子类中重写这些方法,这显然不能很好的解决问题。 将新的行为接口化,看起来能解决问题(例如:只有会飞的鸭子才能继承Flyable),但是Java接口中的方法没有具体实现的代码,继承接口无法达到代码复用。这意味着:无论何时你需要修改某个行为,都必须追中到每一个继承此接口的类中修改它,一不小心,可能造成新的错误。
一个好的设计原则应该找出应用中可能需要变化的地方,把这些方法“封装”起来,不要和那些不需要变化的代码混在一起,好让其他部分不受到它的影响。
解决方法:
1.对于Duck类,分开变化和不会变化的部分。
我们知道Duck类的fly()和quack()会随着鸭子的不同而改变。为了把这两个行为从Duck类中分开,我们将其取出,建立一组新类来代表每个行为。
设计原则:针对接口编程,而不是针对实现编程。
鸭子的行为被放在分开的类中,此类专门提供某行为接口的实现。这样,Duck类就不需要知道行为的实现细节。
下图为我们抽取出Duck类的fly()方法。将其定义为接口,并且有多种具体实现该接口的类。
在这种新的设计方法中,鸭子的子类将使用接口所表现出来的行为(FlyWithWings和FlyNoWay),实际的“实现过程”不会绑死在鸭子的子类中。
抽取Duck类中的quack()方法与之类似这里不再赘述。
2.整合鸭子的行为。
如下图所示,在Duck类中“加入两个实例变量”,声明为接口类型(FlyBehavior和QuackBehavior),每个鸭子对象都会动态的设置这些变量以在运行时引用正确的行为。(例如:FlyWithWings、FlyNoWay)
现在我们来实现performQuack()
public class Duck
QuackBehavior quackBehavior;//接口类型
FlyBehavior flyBehavior;
...省略其他
//具体实现
public void performQuack()
quackBehavior.quack(); //委托给具体行为类
public void performFly()
flyBehavior.fly(); //委托给具体行为类
...省略其他
对于具体子类例如:MallardDuck类。
public class MallardDuck extends Duck
public MallardDuck()
...省略其他,以fly()为例子
flyBehavior =new FlyWithWings();
//继承父类方法
public void display()
System.out.println("I'am a real Mallard duck");
当绿头鸭MallardDuck实例化时,它的构造器会把继承来的flyBehavior实例变量初始化成FlyWithWings类型的实例(FlyWithWings是FlyBehavior的具体实现类),同样的处理方式可以用到其他行为上,这里我们省略,只以fly()为例。
目前的做法已经可以基本实现问题要求,接下来我们继续优化:在鸭子子类中通过“设定方法”(setter method)来设定鸭子行为,而不是在鸭子的构造器中实例化。
Duck类
public class Duck
QuackBehavior quackBehavior;//接口类型
FlyBehavior flyBehavior;
...省略其他
//具体实现
public void performQuack()
quackBehavior.quack(); //委托给具体行为类
public void performFly()
flyBehavior.fly(); //委托给具体行为类
...省略其他
public void setFlyBehavior(FlyBehavior fb)
flyBehavior=fb;
public void setQuackBehavior(QuackBehavior qb)
quackBehavior=qb;
FlyBehavior接口类和其具体行为实现类
//接口类
public interface FlyBehavior
public void fly();
//具体行为实现类
public class FlyWithWings implments FlyBehavior
public void fly()
System.out.println("I'am flying!!");
//具体行为实现类
public class FlyRocketPowered implments FlyBehavior
public void fly()
System.out.println("I'am flying with a rocket!!");
//具体行为实现类
public class FlyNoWay implments FlyBehavior
public void fly()
System.out.println("I can't fly!!");
同理QuackBehavior接口类和其具体行为实现类
//接口类
public interface QuackBehavior
public void quack();
//具体行为实现类
public class Quack implments FlyBehavior
public void quack()
System.out.println("Quack");
//具体行为实现类
public class MuteQuack implments FlyBehavior
public void quack()
System.out.println("I can't quack!!");
具体的子类这里我们列出一个(ModelDuck)
//接口类
public class ModelDuck extends Duck
public ModelDuck()
flyBehavior =new FlyNoWay();
quackBehavior=new Quack();
public void display()
System.out.println("I'am a model duck");
测试程序类DuckSimulator.java
//接口类
public class DuckSimulator
public static void main(String[] args)
Duck model =new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
这样优化,就可以动态的改变子类鸭子的飞行行为。
至此,鸭子模拟器的设计已经完成,如上图所示,我们来总结下封装行为的大局观.
鸭子的子类继承Duck类,飞行行为实现FlyBehavior接口,叫声行为实现QuackBehavior接口。并且定义了setFlayBehavior()和setQuackBheavior()方法,可以动态改变鸭子的行为。这就是应用策略模式的好处。将鸭子的“一组行为”(一族算法)封装起来,可以针对不同的鸭子定义不同的行为(算法),并且行为的变化(算法的变化)也独立于具体的鸭子。
设计原则:多用组合,少用继承。针对接口编程,而不是针对实现编程。
使用组合建立系统具有很大的弹性,不仅可以将算法族封装成类,更可以“在运行时动态地改变行为”,只要组合的行为对象符合正确的接口标准即可。
以上是关于设计模式–策略模式的主要内容,如果未能解决你的问题,请参考以下文章