策略模式是設計模式中很重要的一種,它的主要意圖就是:定義了算法族,分別封裝起來,讓它們之間可以互相替換。它讓算法的變化可以獨立於使用算法的客戶,使得我們可以隨時更改它們而不影響客戶端的代碼,而客戶端可以自由選擇不同的算法執行。
要想了解策略模式,我們就要理解幾個重要概念:
public abstract class Plane{ private abstract void fly(); private abstract void attack(); }
接着我們就開始定義一些具體型號的戰斗機:
public class RocketPlane extends Plane{ private void fly(){ System.out.println("This plane fly with Rocket"); } private void attack(){ System.out.println("This plane attack with fire"); } } public class WindPlane extends Plane{ private void fly(){ System.out.println("This plane fly with wind"); } private void attack(){ System.out.println("This plane attack with Rocket"); } }
然后就在我們的程序中這樣寫:
public static void main(String[] args){ RocketPlane plane1 = new RocketPlane(); WindPlane plane2 = new WindPlane(); showFunctionOfPlane(plane1); showFunctionOfPlane(plane2); } private void showFunctionOfPlane(Plane plane){ plane.fly(); plane.attack(); }
使用繼承可以解決這個問題,但是,繼承也有它自己的問題。繼承最大的問題就是,基類的改變會傳給所有的子類,這是我們類設計者不想看到的。那么,不使用繼承不就可以了?接口,就是我們這種情況下最好的替代方案。
public Interface Plane{ void fly(); void attack(); }
這樣就將具體的實現交給實現類,從而避免我們上面的問題。確實如此,但不同型號的戰斗機,就算外觀差距太大,基本的東西都是不變的,像是重量這些基本的屬性,至少在很長的一段時間都不會發生變化,如果用接口的話,我們就不能設置一些共同的屬性和方法,當然我們可以將這樣的東西交給實現類來實現,這樣,代碼重復的程度太可怕了!
public Interface FlyAble{ void fly(); } public Interface AttackAble{ void attack(); }
public class RocketPlane extends Plane implements FlyAble, AttackAble{ void fly(){
System.out.println("This plane fly with Rocket");
} void attack(){
System.out.println("This plane attack with fire");
} } public class WindPlane extends Plane implements FlyAble, AttackAble{ void fly(){
System.out.println("This plane fly with wind");
} void attack(){
System.out.println("This plane attack with Rocket");
} }
為什么會這樣寫?很簡單,因為我們可能有些飛機根本不具有飛行能力,像是這樣:
public class NotFlyPlane extends Plane implements AttackAble{ void attack(){
System.out.println("This Plane attack with wind");
} }
但是,根本不需要我們的子類實現這些接口,接口更大的意義是對象組合,這樣根本就失去了接口的優點。要想利用接口的這些優點,我們可以這樣建立這兩個接口的實現類組,像是這樣:
public class FlyWithRocket implements FlyAble{ void fly(){
System.out.println("This plane fly with rocket");
} }
public class AttackWithRocket implements AttackAble{ void attack(){
System.out.println("This Plane attack with rocket");
} }
public class FlyWithWind implements FlyAble{
void fly(){
System.out.println("This plane fly with wind");
}
}
public class AttackWithFire implements AttackAble{
void attack(){
System.out.println("This plane attack with fire");
}
}
public class NotFly implements FlyAble{
void fly(){
System.out.println("This plane can't fly");
}
}
然后再在我們的代碼中使用這些實現類:
public abstract class Plane(){
protected FlyAble mFly;
protected AttackAble mAttack;
protected abstract void description();
protected void functionTest(){
mFly.fly();
mAttack.attack();
}
}
public class RocketPlane extends Plane{ RocketPlane(){
super.mFly = new FlyWithRocket(); super.mAttack = new AttackWithFire();
}
protected void description(){
System.out.println("I am a RocketPlane");
}
}
public class test{
public static void main(String[] args){
RocketPlane plane = new RocketPlane();
plane.functionTest();
}
}
這就是使用對象組合的方式,但是這樣的方式還不夠優雅。這時,策略模式就正式登場了,因為它就是處理對象組合的一種模式。
public abstract class Plane {
private FlyAble mFly;
private AttackAble mAttack;
Plane() { }
Plane(FlyAble fly, AttackAble attack) {
this.mFly = fly;
this.mAttack = attack;
}
protected abstract void description();
protected void setFly(FlyAble fly) {
this.mFly = fly;
}
protected void setAttack(AttackAble attack) {
this.mAttack = attack;
}
protected void testFunction() {
description();
mFly.fly();
mAttack.attack();
}
}
接着是我們的子類和測試類:
public class RocketPlane extends Plane {
RocketPlane() { }
RocketPlane(FlyAble fly, AttackAble attack) {
super(fly, attack);
}
@Override
protected void description() {
System.out.println("I am a RocketPlane");
}
}
public class test{
public static void main(String[] args){
RocketPlane plane = new RocketPlane(new FlyWithRocket(), new AttackWithFire());
plane.testFunction();
}
}
plane.setFly(new FlyWithWind());
public class FunctionTest{ private Plane mPlane; FunctionTest(Plane plane){ this.mPlane = plane; } void functionTest(){ plane.testFunction(); } }
public class test{
public static void main(String[] args){
RocketPlane plane = new RocketPlane(new FlyWithRocket(), new AttackWithFire());
FunctionTest test = new FunctionTest(plane);
test.functionTest();
}
}
使用委托類到底有什么好處? 委托類提供的就是一個間接層,我們不需要知道有關於戰斗機的具體細節,我們只知道,使用functionTest()就可以讓我們的戰斗機飛起來,攻擊敵人。這就是封裝,客戶只知道調用委托類提供的方法就可以,就算戰斗機的內部構造發生變化,像是testFunction()變成function(),又和我們客戶代碼有什么關系呢?
