从java多态到策略模式_设计模式中的多态——策略模式详解
2. 策略模式詳解
2.1 策略模式定義
策略模式定義了一系列算法,并將每一個(gè)算法封裝起來(lái),而且使它們還可以相互替換。策略模式讓算法獨(dú)立于使用它的客戶端而獨(dú)立的變化。
可以使用多態(tài)進(jìn)行類比來(lái)理解策略模式的定義。一系列算法可以理解成接口的不同實(shí)現(xiàn)類,因?yàn)椴煌瑢?shí)現(xiàn)類都實(shí)現(xiàn)了相同的接口,因而它們也可以相互替換。策略模式讓算法獨(dú)立于客戶端而變化與接口的實(shí)現(xiàn)類可以獨(dú)立于使用接口的客戶端變化類似。
2.2 策略模式的UML類圖
從UML類圖上可以看出,策略模式中主要有3個(gè)角色
抽象策略接口
上圖中的Strategy即抽象策略接口,接口中定義了抽象的策略算法algorithm()。
具體的策略實(shí)現(xiàn)類
上圖中的StrategyA和StrategyB即具體的策略實(shí)現(xiàn)。不同的策略實(shí)現(xiàn)類都實(shí)現(xiàn)了抽象策略接口,并重寫了其抽象策略方法。因?yàn)槎紝?shí)現(xiàn)了相同的策略接口,因而算法可以相互替換,并且可以動(dòng)態(tài)的改變具體的算法實(shí)現(xiàn)。
封裝策略的上下文環(huán)境
上圖中的Context即策略的上下文環(huán)境。它屏蔽了高層模塊對(duì)策略算法的直接訪問(wèn),封裝了可能存在的變化。而且提供了修改Strategy的setter方法,可以動(dòng)態(tài)的改變算法的具體實(shí)現(xiàn)。
3.策略模式的優(yōu)點(diǎn)
我們可以結(jié)合使用策略模式的例子并與其它實(shí)現(xiàn)方案進(jìn)行對(duì)比來(lái)看看策略模式到底有什么好處
3.1 一個(gè)使用策略模式的例子
定義一個(gè)汽車類Car。由于汽車最大的特點(diǎn)是能跑,因而我們賦予該類一個(gè)move行為。但要跑起來(lái)需要提供能源,通常而言這種能源是汽油,但現(xiàn)在純靠電池驅(qū)動(dòng)的汽車也越來(lái)越多。因而Car的move行為就有兩種不同的行為,一種是使用汽油跑,一種是使用電能跑。因而我們可以這么定義
抽象的汽車類Car
/**
* @author: takumiCX
* @create: 2018-10-13
**/
public abstract class Car {
//汽車品牌
private String brand;
public Car(String brand) {
this.brand = brand;
}
public Car(String brand, MoveStrategy strategy) {
this.brand = brand;
this.moveStrategy=strategy;
}
//汽車的運(yùn)行策略:使用汽油運(yùn)行,使用電能運(yùn)行等等
private MoveStrategy moveStrategy;
//運(yùn)行方法
public void move() {
System.out.print(brand);
moveStrategy.move();
}
public void setMoveStrategy(MoveStrategy moveStrategy) {
this.moveStrategy = moveStrategy;
}
}
在抽象汽車類中定義了一個(gè)move()方法表示汽車具有運(yùn)行的行為,但是由于到底是使用汽油運(yùn)行還是使用電能運(yùn)行并沒(méi)有直接定義在里面,而是調(diào)用了策略接口中定義的move方法。該策略接口以組合的方式封裝在Car內(nèi)部,并提供了setter方法供客戶端動(dòng)態(tài)切換汽車的運(yùn)行方式。
使用汽油運(yùn)行的策略實(shí)現(xiàn)
/**
* @author: takumiCX
* @create: 2018-10-14
**/
/**
* 使用汽油運(yùn)行的策略實(shí)現(xiàn)
*/
public class GasolineMoveStrategy implements MoveStrategy{
@Override
public void move() {
System.out.println(" Use Gasoline Move!");
}
}
使用電池運(yùn)行的策略實(shí)現(xiàn)
/**
* @author: takumiCX
* @create: 2018-10-15
**/
/**
* 使用電能運(yùn)行的策略實(shí)現(xiàn)
*/
public class ElectricityMoveStrategy implements MoveStrategy {
@Override
public void move() {
System.out.println(" Use Electricity Move!");
}
}
具體的汽車實(shí)現(xiàn)類
比如我們通過(guò)繼承的方式定義一輛特斯拉汽車,特斯拉汽車默認(rèn)是純電動(dòng)的
/**
* @author: takumiCX
* @create: 2018-10-13
**/
public class TeslaCar extends Car {
public TeslaCar(String brand) {
super(brand,new ElectricityMoveStrategy());
}
}
客戶端代碼
首先構(gòu)造一輛特斯拉車觀察其運(yùn)行方式,并通過(guò)setter方法動(dòng)態(tài)改變其運(yùn)行方式為汽油驅(qū)動(dòng)
/**
* @author: takumiCX
* @create: 2018-10-13
**/
public class Client {
public static void main(String[] args) {
TeslaCar car = new TeslaCar("Tesla");
car.move();
car.setMoveStrategy(new GasolineMoveStrategy());
car.move();
}
}
運(yùn)行結(jié)果
3.2 與其他實(shí)現(xiàn)方式的對(duì)比
其實(shí)上面的例子除了使用策略模式外,還有其他實(shí)現(xiàn)方式,但它們都有比較明顯的缺點(diǎn)。
3.2.1接口的實(shí)現(xiàn)方式
/**
* @author: takumiCX
* @create: 2018-10-15
**/
public interface Move {
void move();
}
并讓抽象父類Car實(shí)現(xiàn)它
/**
* @author: takumiCX
* @create: 2018-10-13
**/
public abstract class Car implements Move{
//汽車品牌
private String brand;
public Car(String brand) {
this.brand = brand;
}
}
這樣所有繼承Car的具體汽車類都必須實(shí)現(xiàn)自己的move方法,也就是讓具體的汽車子類來(lái)決定汽車的具體行為:到底是使用汽油運(yùn)行還是使用電池運(yùn)行。但是這么做至少有以下幾個(gè)缺點(diǎn)
1.具體的汽車運(yùn)行行為不方便后期維護(hù)。因而move行為無(wú)法被復(fù)用,具體的實(shí)現(xiàn)都分散在了子類中。如果要對(duì)某種驅(qū)動(dòng)方式的實(shí)現(xiàn)進(jìn)行修改,不得不修改所有子類,這簡(jiǎn)直是災(zāi)難。
2.導(dǎo)致類數(shù)量的膨脹。同樣品牌的汽車,由于有汽油和電動(dòng)兩種運(yùn)行方式,不得不為其維護(hù)兩個(gè)類,如果在增加一種驅(qū)動(dòng)方式,比如氫能源驅(qū)動(dòng),那不得為每個(gè)品牌的汽車再增加一個(gè)類。
3.不方便move行為的擴(kuò)展,也不方便動(dòng)態(tài)的更換其實(shí)現(xiàn)方式。
3.2.2 if-else的實(shí)現(xiàn)方式
move方法接受客戶端傳遞的參數(shù),通過(guò)if-else或者swich-case進(jìn)行判斷,選擇正確的驅(qū)動(dòng)方式。
public void move(String moveStrategy) {
if("electricity".equals(moveStrategy)){
System.out.println(" Use Electricity Move!");
}else if("gasoline".equals(moveStrategy)){
System.out.println(" Use Gasoline Move!");
}
}
但這樣做相當(dāng)于硬編碼,不符合開閉原則。比如我要增加一種氫能源的驅(qū)動(dòng)方式,這種實(shí)現(xiàn)就需要修改move中的代碼。而如果使用上面說(shuō)的策略模式,則只需要增加一個(gè)實(shí)現(xiàn)實(shí)現(xiàn)策略接口的具體策略實(shí)現(xiàn)類,而不需要修改move中的任何代碼,即可被客戶端所使用。
/**
* @author: takumiCX
* @create: 2018-10-15
**/
public class HydrogenMovetrategy implements MoveStrategy {
@Override
public void move() {
System.out.println(" Use Hydrogen Move!");
}
}
3.3 使用策略模式的優(yōu)點(diǎn)
1.可以優(yōu)化類結(jié)構(gòu),當(dāng)類的某種功能有多種實(shí)現(xiàn)時(shí),可以在類中定義策略接口,將真正的功能實(shí)現(xiàn)委托給具體的策略實(shí)現(xiàn)類。這樣避免了類膨脹,也能更好的進(jìn)行擴(kuò)展和維護(hù)。
2.避免使用多重條件判斷導(dǎo)致的硬編碼和擴(kuò)展性差的問(wèn)題
3.可以使具體的算法實(shí)現(xiàn)自由切換,增強(qiáng)程序設(shè)計(jì)的彈性。
4. 使用工廠方法模式改進(jìn)原有策略模式
所有的策略實(shí)現(xiàn)都需要對(duì)外暴露,上層模塊必須知道具體的策略實(shí)現(xiàn)類,這與迪米特法則相違背。為此,可以使用工廠方法模式進(jìn)行解耦。
策略工廠接口
/**
* @author: takumiCX
* @create: 2018-10-16
**/
public interface MoveStrategyFactory {
MoveStrategy create();
}
氫能源驅(qū)動(dòng)方式的工廠
/**
* @author: takumiCX
* @create: 2018-10-16
**/
public class HydrogenMoveStrategyFactory implements MoveStrategyFactory {
@Override
public MoveStrategy create() {
return new HydrogenMovetrategy();
}
}
客戶端
/**
* @author: takumiCX
* @create: 2018-10-13
**/
public class Client {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
TeslaCar car = new TeslaCar("Tesla");
MoveStrategyFactory factory = new HydrogenMoveStrategyFactory();
MoveStrategy moveStrategy = factory.create();
car.setMoveStrategy(moveStrategy);
car.move();
}
}
這樣我們通過(guò)工廠方法模式封裝了具體策略類的創(chuàng)建過(guò)程,同時(shí)也避免了向高層模塊暴露。最后運(yùn)行結(jié)構(gòu)如下
5. 總結(jié)
當(dāng)完成某項(xiàng)功能有多種不同的實(shí)現(xiàn)時(shí),可以實(shí)用策略模式。策略模式封裝了不同的算法,并且使這些算法可以相互替換,這提高了代碼的復(fù)用率也增強(qiáng)了程序設(shè)計(jì)的彈性。并且可以結(jié)合其他設(shè)計(jì)模式比如工廠方法模式向上層模塊屏蔽具體的策略類,使代碼更易于擴(kuò)展和維護(hù)。
5. 參考資料
《Head First 設(shè)計(jì)模式》
《設(shè)計(jì)模式之禪》
總結(jié)
以上是生活随笔為你收集整理的从java多态到策略模式_设计模式中的多态——策略模式详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux iptables找不到,ce
- 下一篇: linux 喂狗时间,狗狗正确喂食时间表