1.定義
策略模式定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。
這些算法族可以看成對象的一系列行爲。
2.場景描述
現在要開發一款鴨子游戲,目前是所有的鴨子都會游泳,但是綠頭鴨(MallardDuck)不會呱呱叫,只會吱吱叫;橡皮鴨不會飛等。後續還有可能會出現唐老鴨角色,即不會飛,也不會吱吱叫,呱呱叫,但是會說話等突然情況需要添加或修改遊戲角色。
3.設計類關係圖
4.代碼實現
1)Duck抽象類
public abstract class Duck {
/**
* 爲行爲接口類型聲明兩個引用變量,所有duck子類都繼承
* 聲明爲接口類型,這樣每個duck對象都能動態的設置這些變量,在運行時引用正確的行爲類型
*/
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){
}
public abstract void display();
public void performFly(){
//委託給行爲類
flyBehavior.fly();
}
public FlyBehavior getFlyBehavior() {
return flyBehavior;
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public QuackBehavior getQuackBehavior() {
return quackBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public void performQuack(){
//委託給行爲類
quackBehavior.quack();
}
public void swim(){
System.out.println("所有的鴨子都會游泳");
}
}
2)MallardDuck類
public class MallardDuck extends Duck {
/**
* 因爲MallardDuck 繼承Duck,所以具有quackBehavior和flyBehavior實例變量
* MallardDuck 使用Quack類處理呱呱叫,當performQuack()被調用時,叫的職責被委託給Quack對象
* Fly行爲也一樣
*/
public MallardDuck(){
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("I`m a real Mallard duck");
}
}
3)FlyBehavior接口
/**
* 這是一個接口,所有飛行類都要實現它,所有新的飛行類都必須實現fly方法
* Quack接口也一樣
*/
public interface FlyBehavior {
void fly();
}
4)FlyWithSwing類
public class FlyWithWings implements FlyBehavior {
/**
* 這是飛行行爲的實現,給會飛的duck使用
*/
public void fly() {
System.out.println("我飛起來了");
}
}
5)FlyNoWay類
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("不會飛");
}
}
6)QuackBehavior接口
public interface QuackBehavior {
void quack();
}
7)Quack類
public class Quack implements QuackBehavior{
public void quack() {
System.out.println("呱呱叫");
}
}
8)MuteQuack類
public class MuteQuack implements QuackBehavior{
public void quack() {
System.out.println("不會叫");
}
}
9)測試類
public class DuckTest {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
//這時會調用MallardDuck繼承來的performFly()方法,委託給該對象
//的QuackBehavior對象處理,即FlyWithWings類調用fly()方法
mallard.performFly();
mallard.performQuack();
}
}
5.策略模式遵循的oo設計原則
1)找出應用中可能需要變換之處,把它們獨立出來
2)針對接口編程,不針對實現編程
3)多用組合,少用繼承
在上述的代碼中,你會發現MallardDuck的構造器中是針對具體實現編程,但是我們在Duck類提供了FlyBehavior和QuackBehavior的set()方法,可以實現在運行時動態改變行爲類。此時的測試類修改爲:
public class DuckTest {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
//這時會調用MallardDuck繼承來的performFly()方法,委託給該對象
//的QuackBehavior對象處理,即FlyWithWings類調用fly()方法
mallard.performFly();
mallard.performQuack();
mallard.setFlyBehavior(new FlyNoWay());
mallard.setQuackBehavior(new MuteQuack());
System.out.println("動態改變行爲類了,你看!");
mallard.performFly();
mallard.performQuack();
}
}
運行結果
如果遊戲需要添加一種利用動力的飛行行爲,這時只需要將實現飛行行爲的具體實現實現FlyBehavior接口即可,這樣某個Duck子類具有該飛行行爲時可以使用該類。
6.策略模式的缺點
此刻你會發現,如果要添加某一種行爲,就需要不斷的添加子類,所以使用策略模式可能會產生大量的類。
最後,再來回顧一下策略模式:封裝可以互換的行爲,並使用委託來決定要使用哪一個。