一、前言
笔者在看JDK源码的同时也穿插着看设计模式,之前有涉猎设计模式,但是没有进行总结和提炼,现在再读一遍设计模式,感觉受益匪浅,也特此进行记录。下面设计模式系列是以《Head First 设计模式》书为参考。有兴趣的读者可以购买,讲解得浅显易懂。
二、策略模式
定义:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
乍一看,概念比较难懂和生涩,可以用另外一种方式说明,对于不同的策略,对象会有不同的行为,这些策略是独立的,并且可以替换彼此,下面通过实际的示例来帮助理解这一模式。
三、示例说明
设计一个鸭子游戏,游戏中有各种鸭子,鸭子有各种行为,如呱呱叫、游泳等,同时不同的鸭子的外观不相同,这时候,很容易设计如下的类图。
说明:Duck为抽象类,表示鸭子类,所有的鸭子都有呱呱叫和游泳的行为,所以在Duck抽象类中实现,而display则是抽象的,因为不同的鸭子有不同的外观,具体子类给出实现。MallardDuck、RedheadDuck是呱呱叫(与Duck实现的相同),两者的外观不相同。RubberDuck是吱吱叫(需要重写Duck的quack方法),外观也不相同。
3.1 v1.0
根据类图,代码如下
Duck类
package com.hust.grid.leesf.strategy; public abstract class Duck { public void quack() { System.out.println("quack..."); } public void swim() { System.out.println("swim..."); } public abstract void display(); }
MallardDuck类
package com.hust.grid.leesf.strategy; public class MallardDuck extends Duck { @Override public void display() { System.out.println("I am a MallardDuck"); } }
RedheadDuck类
package com.hust.grid.leesf.strategy; public class RedheadDuck extends Duck { @Override public void display() { System.out.println("I am a RedheadDuck"); } }
RubberDuck类
package com.hust.grid.leesf.strategy; public class RubberDuck extends Duck { @Override public void display() { System.out.println("I am a RubberDuck"); } @Override public void quack() { System.out.println("squeak..."); } @Override public void fly() { } }
Main类(用作测试)
package com.hust.grid.leesf.strategy; public class Main { public static void main(String[] args) { Duck duck = new MallardDuck(); duck.quack(); duck.swim(); duck.display(); System.out.println("------------------"); duck = new RedheadDuck(); duck.quack(); duck.swim(); duck.display(); System.out.println("------------------"); duck = new RubberDuck(); duck.quack(); duck.swim(); duck.display(); } }
运行结果:
quack...
swim...
I am a MallardDuck
------------------
quack...
swim...
I am a RedheadDuck
------------------
squeak...
swim...
I am a RubberDuck
可以看到,上面的编写的代码可以运行良好,Rubber类重写了quack,为吱吱叫,但是,若此时提出一个新的需求,给某些鸭子添加飞的行为,因为有些可以飞(会飞的行为),一种不太好的解决办法是在Duck超类中添加fly方法,这样,每个子类都将会有fly方法,达到了代码复用的目的,但是,对与RubberDuck而言,它不会飞。则可以将RubberDuck类的fly覆盖,什么都不做。
3.2 v2.0
v2.0,有些鸭子会飞(有飞的行为),修改代码如下
Duck类
package com.hust.grid.leesf.strategy; public abstract class Duck { public void quack() { System.out.println("quack..."); } public void swim() { System.out.println("swim..."); } public abstract void display(); public void fly() { System.out.println("fly..."); } }
RubberDuck类
package com.hust.grid.leesf.strategy; public class RubberDuck extends Duck { @Override public void display() { System.out.println("I am a RubberDuck"); } @Override public void quack() { System.out.println("squack..."); } @Override public void fly() { } }
Main类(用于测试)
package com.hust.grid.leesf.strategy; public class Main { public static void main(String[] args) { Duck duck = new MallardDuck(); duck.quack(); duck.swim(); duck.display(); duck.fly(); System.out.println("------------------"); duck = new RedheadDuck(); duck.quack(); duck.swim(); duck.display(); duck.fly(); System.out.println("------------------"); duck = new RubberDuck(); duck.quack(); duck.swim(); duck.display(); duck.fly(); } }
运行结果:
quack... swim... I am a MallardDuck fly... ------------------ quack... swim... I am a RedheadDuck fly... ------------------ squack... swim... I am a RubberDuck
RubberDuck重写了Duck类fly方法,使其什么都不做,这样可以认为RubberDuck不具有飞的行为。
不足:当新增子类时,若子类也不具有飞的行为,则又要像RubberDuck一样进行处理,即每当新增子类时,都需要检查是否需要覆盖quack方法和fly方法,这显然是很麻烦的。有没有一种更好的办法,可以很好的解决这个问题呢?
3.3 v3.0
分离鸭子行为中变化的与不变化的部分(设计原则:找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起),这样,每次有新的需求时,都会使某方面的代码发生变化,可以把这部分的代码抽离出来,和其他的稳定代码有所区别。方便进行改动或者扩充,而不影响不需要变化的部分。变化的部分包括飞行行为和呱呱叫行为。
1. 抽出鸭子飞行行为
2. 抽出鸭子呱呱叫行为
设计的类图如下图所示
说明:对于飞行行为和呱呱叫行为都已经从鸭子对象本身独立出来了。这样,可以很轻易让鸭子对象有其他的行为。Duck对象中有FlyBehavior和QuackBehavior两个实例变量。
再次修改后的代码如下
Duck类
package com.hust.grid.leesf.strategy; public abstract class Duck { private FlyBehavior flyBehavior; private QuackBehavior quackBehavior; public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } public void performQuack() { quackBehavior.quack(); } public void performFly() { flyBehavior.fly(); } public void swim() { System.out.println("swim..."); } public abstract void display(); }
MallardDuck类
package com.hust.grid.leesf.strategy; public class MallardDuck extends Duck { public void display() { System.out.println("I am a MallardDuck"); } }
RedheadDuck类
package com.hust.grid.leesf.strategy; public class RedheadDuck extends Duck { @Override public void display() { System.out.println("I am a RedheadDuck"); } }
RubberDuck类
package com.hust.grid.leesf.strategy; public class RubberDuck extends Duck { @Override public void display() { System.out.println("I am a RubberDuck"); } }
FlyBehavior类
package com.hust.grid.leesf.strategy; public interface FlyBehavior { void fly(); }
FlyWithWings类
package com.hust.grid.leesf.strategy; public class FlyWithWings implements FlyBehavior { @Override public void fly() { System.out.println("fly with wings"); } }
FlyNoWay类
package com.hust.grid.leesf.strategy; public class FlyNoWay implements FlyBehavior { @Override public void fly() { } }
QuackBehavior类
package com.hust.grid.leesf.strategy; public interface QuackBehavior { void quack(); }
Quack类
package com.hust.grid.leesf.strategy; public class Quack implements QuackBehavior { @Override public void quack() { System.out.println("quack..."); } }
Squeak类
package com.hust.grid.leesf.strategy; public class Squeak implements QuackBehavior { @Override public void quack() { System.out.println("squeak..."); } }
MuteQuack类
package com.hust.grid.leesf.strategy; public class MuteQuack implements QuackBehavior { @Override public void quack() { } }
Main类
package com.hust.grid.leesf.strategy; public class Main { public static void main(String[] args) { Duck duck = new MallardDuck(); FlyBehavior flyBehavior = new FlyWithWings(); duck.setFlyBehavior(flyBehavior); QuackBehavior quackBehavior = new Quack(); duck.setQuackBehavior(quackBehavior); duck.performQuack(); duck.performFly(); duck.swim(); duck.display(); System.out.println("------------------"); } }
运行结果:
quack...
fly with wings
swim...
I am a MallardDuck
------------------
可以看到,这个版本的鸭子游戏已经比较理想,此时,可以很容易的添加子类,并且不用改变之前稳定的代码,在运行过程中可以动态改变对象的行为(通过setFlyBehavior和setQuackBehavior实现)。如添加一个ModelDuck类,继承自Duck类,FlyRocketPower,实现FlyBehavior接口,代码如下
ModelDuck类
package com.hust.grid.leesf.strategy; public class ModelDuck extends Duck { @Override public void display() { System.out.println("I am a ModelDuck"); } }
FlyRocketPower类
package com.hust.grid.leesf.strategy; public class FlyRocketPower implements FlyBehavior { @Override public void fly() { System.out.println("fly with a rocket"); } }
Main类(用作测试)
package com.hust.grid.leesf.strategy; public class Main { public static void main(String[] args) { Duck duck = new MallardDuck(); FlyBehavior flyBehavior = new FlyWithWings(); duck.setFlyBehavior(flyBehavior); QuackBehavior quackBehavior = new Quack(); duck.setQuackBehavior(quackBehavior); duck.performQuack(); duck.performFly(); duck.swim(); duck.display(); System.out.println("------------------"); duck = new ModelDuck(); flyBehavior = new FlyRocketPower(); quackBehavior = new MuteQuack(); duck.setFlyBehavior(flyBehavior); duck.setQuackBehavior(quackBehavior); duck.performQuack(); duck.performFly(); duck.swim(); duck.display(); System.out.println("------------------"); } }
运行结果
quack... fly with wings swim... I am a MallardDuck ------------------ fly with a rocket swim... I am a ModelDuck ------------------
四、总结
策略模式的应用很广泛,在游戏中有应用,如角色可以更换不同的武器,不同的武器有不同的效果,就可以使用策略模式来完成。所有源代码已经上传至github,欢迎fork,谢谢各位园友的观看~