近日拿出 Design Pattern 的書重溫,順便和大家分享一下。
Strategy Pattern 主要的目的是將一些有很大機會轉變的行為或演算法封裝起來,並抽出來, 令到在執行期間也可以替換。這樣做的好處是不用變動核心程式碼,而又做到變更行為等等功能。
以下的例子可能令你更明白這個模式的用途。
你的公司要你開發一個遊戲,這個遊戲是一個古代戰爭遊戲,每個人扮演一個特定角色。 公司希望可以隨時增加角色,你一開始的設計可能會這樣:
(Character.java) download public abstract class Character {
private String name;
private int hp;
private int mp;
public Character(String name, int hp, int mp) {
this.name = name;
this.hp = hp;
this.mp = mp;
}
public void attack() {
System.out.println("Attack");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getMp() {
return mp;
}
public void setMp(int mp) {
this.mp = mp;
}
}
|
(Human.java) download public class Human extends Character {
public Human(String name) {
super(name, 100, 100);
}
}
|
(Dwarve.java) download public class Dwarve extends Character {
public Dwarve(String name) {
super(name, 150, 50);
}
}
|
這樣的設計原本符合公司規格,隨時可以新增角色,亦利用了 OO 的特性。
公司高層認為每個角色的攻擊都一樣,太乏味了,建議你改一改程式去令每一個角色都拿不同武器去攻擊。 你可能會更改程式成為這樣:
(Character.java) download public abstract class Character {
private String name;
private int hp;
private int mp;
public Character(String name, int hp, int mp) {
this.name = name;
this.hp = hp;
this.mp = mp;
}
public abstract void attack();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getMp() {
return mp;
}
public void setMp(int mp) {
this.mp = mp;
}
}
|
(Human.java) download public class Human extends Character {
public Human(String name) {
super(name, 100, 100);
}
@Override
public void attack() {
System.out.println("Sword attack");
}
}
|
(Dwarve.java) download public class Dwarve extends Character {
public Dwarve(String name) {
super(name, 150, 50);
}
@Override
public void attack() {
System.out.println("Axe attack");
}
}
|
Character 內的 attack() 改為 abstract ,每個角色自己實現自己的攻擊模式。 這樣看好像沒有什麼問題,但如果以後增加角色,而這個角色和現有角色的武器一樣,那不是要將程式碼複製出來? 同樣的程式碼可能會在不同角色內出現,以後維護起來便加倍困難。複製一次程式碼 = 連 Bug 也一起複製了。
公司高層又想改一下遊戲,今次改動是可以令角色選擇不同武器。 上次的程式是不容許角色更換武器,因為在 attack() 內限制了角色使用的武器。 今次需要使用 Strategy Pattern 令程式更具彈性:
(Character.java) download public abstract class Character {
private String name;
private int hp;
private int mp;
private AttackBehavior attackBehavior;
public Character(String name, int hp, int mp, AttackBehavior attackBehavior) {
this.name = name;
this.hp = hp;
this.mp = mp;
this.attackBehavior = attackBehavior;
}
public void attack() {
attackBehavior.attack();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getMp() {
return mp;
}
public void setMp(int mp) {
this.mp = mp;
}
public AttackBehavior getAttackBehavior() {
return attackBehavior;
}
public void setAttackBehavior(AttackBehavior attackBehavior) {
this.attackBehavior = attackBehavior;
}
}
|
(Human.java) download public class Human extends Character {
public Human(String name, AttackBehavior attackBehavior) {
super(name, 100, 100, attackBehavior);
}
}
|
(Dwarve.java) download public class Dwarve extends Character {
public Dwarve(String name, AttackBehavior attackBehavior) {
super(name, 150, 50, attackBehavior);
}
}
|
(AttackBehavior.java) download public interface AttackBehavior {
public void attack();
}
|
(SwordAttackBehavior.java) download public class SwordAttackBehavior implements AttackBehavior {
@Override
public void attack() {
System.out.println("Sword attack");
}
}
|
(AxeAttackBehavior.java) download public class AxeAttackBehavior implements AttackBehavior {
@Override
public void attack() {
System.out.println("Axe attack");
}
}
|
(Main.java) download public class Main {
public static void main(String[] args) {
Character character1 = new Human("Lawrence", new AxeAttackBehavior());
Character character2 = new Human("Tony", new SwordAttackBehavior());
character1.attack();
character2.attack();
character1.setAttackBehavior(new SwordAttackBehavior());
character1.attack();
}
}
|
現在只需要在建立角色時將攻擊行為也一起加到角色中就可以了,而且可以隨時更改攻擊行為。
以下的圖片是描述 Strategy Pattern 的 Class Diagram。(From Wiki)