策略模式(Strategy)行为型模式
策略模式模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户中。
OO原则:针对接口编程,而不是针对实现编程。
针对接口:指的是制造其它类专门实现接口,由行为类来实现行为接口,指的就是针对超类型编程。
针对实现:指的就是具体实现超类方法或者继承某个接口并且由子类实现。
下面就拿Duck例子来说明策略模式:
以前的做法:行为来自Duck超类的具体实现,或是继承某个接口并且由子类自行实现而来;这两种做法都是依赖于“实现”。
新设计:鸭子的子类将使用接口所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类(MallarDuck、RedheadDuck等)中,换句话说,特定的具体行为编写在实现了FlyBehavior与QuackBehavior中。
从现在开始,鸭子的行为将被放在分开的类中,此类专门提供某行为接口的实现。这样,鸭子类就不再需要知道行为的实现细节。
下面是我们要使用的例子Duck的UML图:
Duck的UML图
下面是UML图中每个部分的具体代码:
Duck类
/*
Duck类
*/
public abstract class Duck
{
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){}
public abstract void display();
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void swim(){
System.out.println("All ducks float ever decoys!");
}
// 通过setter method来设定鸭子的行为,而不是在鸭子构造器内实例化
public void setFlyBehavior(FlyBehavior fb){
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb){
quackBehavior = qb;
}
}
FlyBehavior接口与两个行为实现类(FlyWithWings.java与FlyNoWay.java)
/*
FlyBehavior接口与两个行为实现类(FlyWithWings.java与FlyNoWay.java)
*/
public interface FlyBehavior
{
public void fly();
}
public class FlyWithWings implements FlyBehavior
{
public void fly(){
System.out.println("I'm flying");
}
}
public class FlyNoWay implements FlyBehavior
{
public void fly(){
System.out.println("I can't flying");
}
}
QuackBehavior接口及其三个实现类(Quack.java,MuteQuack.java,Squeak.java)
/*
QuackBehavior接口及其三个实现类(Quack.java,MuteQuack.java,Squeak.java)
*/
public interface QuackBehavior
{
public void quack();
}
public class Quack implements QuackBehavior
{
public void quack(){
System.out.println("Quack");
}
}
public class MuteQuack implements QuackBehavior
{
public void quack(){
System.out.println("<< Silence >>");
}
}
public class Squeak implements QuackBehavior
{
public void quack(){
System.out.println("Sqeak");
}
}
MallardDuck类
/*
MallardDuck类
绿头鸭使用Quack类来处理呱呱叫,所以当performQuack()被调用的时候,叫的职责被委托给Quack对象,而我们得到的了真正的呱呱叫
*/
public class MallardDuck extends Duck
{
// 别忘了,因为MallardDuck继承了Duck类,所以具有flyBehavior和quackBehavior实例变量
public MallardDuck(){
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display(){
System.out.println("I'm a real Mallard Duck~");
}
}
测试类
/*
测试类
*/
class MiniDuckSimulator
{
public static void main(String[] args)
{
Duck mallard = new MallardDuck();
mallard.performQuack(); // 调用继承来的quackBehavior引用对象的quack()
mallard.performFly();
}
}
结果:
下面我们做些变化来展现策略模式的优点
创建一个新的鸭子类:模型鸭(ModelDuck.java)
/*
创建一个新的鸭子类:模型鸭(ModelDuck.java)
*/
public class ModelDuck extends Duck
{
public ModelDuck(){
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
public void display(){
System.out.println("I'm a model duck");
}
}
建立一个新的FlyBehavior类型(FlyRocketPoered.java)
/*
建立一个新的FlyBehavior类型(FlyRocketPoered.java)
*/
public class FlyRocketPowered implements FlyBehavior
{
// 建立了一个利用火箭动力的飞行行为
public void fly(){
System.out.println("I'm flying with a rocket!");
}
}
对原先的测试进行以下修改:
通过set方法来动态设置需要改变的模块
加上模型鸭(ModelDuck),并使模型鸭拥有火箭动力
修改后的测试类
/*
测试类
*/
class MiniDuckSimulator
{
public static void main(String[] args)
{
Duck mallard = new MallardDuck();
mallard.performQuack(); // 调用继承来的quackBehavior引用对象的quack()
mallard.performFly();
System.out.println("下面是模型鸭的动态添加功能!");
// 改变测试类,通过set方法来动态设置需要改变的模块
// 加上模型鸭(ModelDuck),并使模型鸭拥有火箭动力
Duck model = new ModelDuck();
model.performFly();
// 调用继承来的setter方法,把火箭动力飞行的行为设定到模型鸭中。
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
}
}
实验结果:
实验总结:
在运行时想改变鸭子的行为,只需调用鸭子的setter方法即可,从而实现了在运行时动态的改变行为。
从策略模式中得到的总结:
以上就是策略模式的应用之一,在上面的那个例子中,将FlyBehavior和QuackBehavior等行为的具体实现都封装起来,并且可以相互替换(通过setter方法),此模式让算法的变化独立于使用算法的客户。满足了针对接口编程,而不是针对实现编程,即由行为类(Quack、Squeak、MuteQuack等)来实现接口(FlyBehavior和QuackBehavior)而不是Duck类来实现行为接口,所以实际的“实现”(RedheadDuck、ModelDuck等)不会被绑死在鸭子的子类中,从而提高了代码的弹性。