說實(shí)話這個(gè)模式挺令人糾結(jié)的,但從這個(gè)模式的定義上來看渠啊,有點(diǎn)讓人摸不到什么頭腦输吏,而且查看資料以后會(huì)發(fā)現(xiàn)還是有點(diǎn)稀里糊涂的,說懂了吧也很簡(jiǎn)單替蛉,也不懂吧也有不懂的理由贯溅,于是查閱手頭的各種書籍,在此寫下心得體會(huì)躲查,算是加深一下印象它浅。
命令模式UML:
命令模式的定義:將請(qǐng)求封裝成一個(gè)對(duì)象,從而讓用戶使用不同的請(qǐng)求把客戶端參數(shù)化熙含,以及支持可撤銷和恢復(fù)的功能罚缕。
定義的理解:
請(qǐng)求:
客戶端要求系統(tǒng)執(zhí)行的操作,在java的世界里面就是某個(gè)對(duì)象的方法怎静。
Command:
請(qǐng)求封裝成的對(duì)象,該對(duì)象是命令模式的主角邮弹。也就是說將請(qǐng)求方法封裝成一個(gè)命令對(duì)象黔衡,通過操作命令對(duì)象來操作請(qǐng)求方法。在命令模式是有若干個(gè)請(qǐng)求的腌乡,需要將這些請(qǐng)求封裝成一條條命令對(duì)象盟劫,客戶端只需要調(diào)用不同的命令就可以達(dá)到將請(qǐng)求參數(shù)化的目的。將一條條請(qǐng)求封裝成一條條命定對(duì)象之后与纽,客戶端發(fā)起的就是一個(gè)個(gè)命令對(duì)象了侣签,而不是原來的請(qǐng)求方法!
Receiver:
有命令急迂,當(dāng)然有命令的接收者對(duì)象:如果有只有命令影所,沒有接受者,那不就是光棍司令了僚碎?沒有電視機(jī)或者電腦主機(jī)猴娩,你對(duì)著電視機(jī)遙控器或者電腦鍵盤狂按有毛用?Receiver對(duì)象的主要作用就是受到命令后執(zhí)行對(duì)應(yīng)的操作勺阐。對(duì)于點(diǎn)擊遙控器發(fā)起的命令來說卷中,電視機(jī)就是這個(gè)Receiver對(duì)象,比如按了待機(jī)鍵渊抽,電視機(jī)收到命令后就執(zhí)行了待機(jī)操作蟆豫,進(jìn)入待機(jī)狀態(tài)。
Client:
但是有一個(gè)問題擺在眼前懒闷,命令對(duì)象現(xiàn)在已經(jīng)有了十减,但是誰來負(fù)責(zé)創(chuàng)建命令呢?這里就引出了客戶端Client對(duì)象愤估,再命令模式中命令是有客戶端來創(chuàng)建的嫉称。打個(gè)比方來說,操作遙控器的那個(gè)人灵疮,就是扮演的客戶端的角色。人按下遙控器的不同按鍵壳繁,來創(chuàng)建一條條命令震捣。
Invoker:
現(xiàn)在創(chuàng)建命令的對(duì)象Client也已經(jīng)露臉了,它負(fù)責(zé)創(chuàng)建一條條命令闹炉,那么誰來使用或者調(diào)度這個(gè)命令呢蒿赢?--命令的使用者就是Invoker對(duì)象了,還是拿人渣触,遙控器羡棵,電視機(jī)來做比喻,遙控器就是這個(gè)Invoker對(duì)象嗅钻,遙控器負(fù)責(zé)使用客戶端創(chuàng)建的命令對(duì)象皂冰。該Invoker對(duì)象負(fù)責(zé)要求命令對(duì)象執(zhí)行請(qǐng)求店展,通常會(huì)持有命令對(duì)象,可以持有很多的命令對(duì)象秃流。
上面的講解著實(shí)有些啰嗦赂蕴,下面就用看電視的人(Watcher),電視機(jī)(Television),遙控器(TeleController)來模擬一下這個(gè)命令模式,其中Watcher是Client角色,Television是Receiver角色舶胀,TeleController是Invoker角色概说。
首先設(shè)計(jì)一個(gè)簡(jiǎn)單的電視機(jī)的對(duì)象:
//電視機(jī)對(duì)象:提供了播放不同頻道的方法
public class Television {
public void playCctv1() {
System.out.println("--CCTV1--");
}
public void playCctv2() {
System.out.println("--CCTV2--");
}
public void playCctv3() {
System.out.println("--CCTV3--");
}
public void playCctv4() {
System.out.println("--CCTV4--");
}
public void playCctv5() {
System.out.println("--CCTV5--");
}
public void playCctv6() {
System.out.println("--CCTV6--");
}
}
電視機(jī)的類創(chuàng)建好了,本文會(huì)以“非命令模式“和“命令模式“兩種實(shí)現(xiàn)看電視的不同之處嚣伐,加深對(duì)命令模式的理解糖赔。
非命令模式實(shí)現(xiàn):
如果不用命令模式的話,其實(shí)實(shí)現(xiàn)看電視的功能很簡(jiǎn)單轩端,首先設(shè)計(jì)一個(gè)看電視的人的類:Watcher放典;既然要看電視,所以Watcher內(nèi)部需要持有一個(gè)電視機(jī)的引用船万。如此簡(jiǎn)單的Watcher搞定:
//觀看電視的死宅類
public class Watcher {
//持有一個(gè)
public Television tv;
public Watcher(Television tv) {
this.tv = tv;
}
public void playCctv1() {
tv.playCctv1();
}
public void playCctv2() {
tv.playCctv2();
}
public void playCctv3() {
tv.playCctv3();
}
public void playCctv4() {
tv.playCctv4();
}
public void playCctv5() {
tv.playCctv5();
}
public void playCctv6() {
tv.playCctv6();
}
}
所以簡(jiǎn)單的調(diào)用就實(shí)現(xiàn)了:
public static void main(String args[]) {
Watcher watcher = new Watcher(new Television());
watcher.playCctv1();
watcher.playCctv2();
watcher.playCctv3();
watcher.playCctv4();
watcher.playCctv5();
watcher.playCctv6();
}
執(zhí)行結(jié)果:
--CCTV1--
--CCTV2--
--CCTV3--
--CCTV4--
--CCTV5--
--CCTV6--
可以看出Watcher類和Television完全的耦合在一起了刻撒,目前本文的電視機(jī)對(duì)象只能播放六個(gè)電視臺(tái),如果需要增添全國所有主流衛(wèi)視的話耿导,需要做如下改動(dòng):
1声怔、修改Television對(duì)象,增加若干個(gè)的playXXTV()的方法來播放不同的衛(wèi)視舱呻。
2醋火、修改Watcher,也添加若干個(gè)對(duì)應(yīng)的playXXTV()的方法箱吕,調(diào)用Television的playXXTV()芥驳,如下:
public void playXXTV() {
tv.playXXTV();
}
但是這明顯違背了“對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放“的重要設(shè)計(jì)原則茬高。
別的不說兆旬,就拿本看電視來說,比如調(diào)用playXXTV()的順序是隨即的怎栽,也就是你胡亂切換了一通:比如你沿著cctv1丽猬、cctv2、cctv3熏瞄、cctv4脚祟、xxtv、yytv…nntv的順序來看電視强饮,也就是發(fā)生了如下調(diào)用:
watcher.playCctv1();
watcher.playCctv2();
watcher.playCctv3();
watcher.playCctv4();
watcher.playCctv5();
watcher.playCctv6();
watcher.playXXtv();
watcher.playYYtv();
watcher.playNNtv();
由桌,當(dāng)前你在看nntv,如果你想看上一個(gè)看過的臺(tái)也就是yytv,這很簡(jiǎn)單,只要在playNNtv() 后面,調(diào)用watcher.playYYtv();即可行您,但是如果你想要一直回退到cctv1呢铭乾?那么你就話發(fā)生如下可怕的調(diào)用:
watcher.playCctv1();
watcher.playCctv2();
watcher.playCctv3();
watcher.playCctv4();
watcher.playCctv5();
watcher.playCctv6();
watcher.playXXtv();
watcher.playYYtv();
watcher.playNNtv();
watcher.playYYtv();
watcher.playXXtv();
watcher.playCctv6();
watcher.playCctv5();
watcher.playCctv4();
watcher.playCctv3();
watcher.playCctv2();
watcher.playCctv1();
為什么會(huì)這樣呢?因?yàn)閷?duì)于之前播放的哪個(gè)衛(wèi)視并沒有記錄功能邑雅。是時(shí)候讓命令模式來出來解決 問題了片橡,通過命令模式的實(shí)現(xiàn),對(duì)比下就能體會(huì)到命令模式的巧妙之處淮野。
命令模式的實(shí)現(xiàn):
1捧书、設(shè)計(jì)一個(gè)抽象的命令類:
在本系統(tǒng)中,命令的接收者對(duì)象就是電視機(jī)Tevevision了:
public abstract class Command {
//命令接收者:電視機(jī)
protected Television television;
public Command(Television television) {
this.television = television;
}
//命令執(zhí)行
abstract void execute();
}
將播放各個(gè)衛(wèi)視的操作封裝成一個(gè)一個(gè)命令骤星,實(shí)現(xiàn)如下:
//播放cctv1的命令
public class CCTV1Command extends Command {
@Override
void execute() {
television.playCctv1();
}
}
//播放cctv2的命令
public class CCTV6Command extends Command {
@Override
void execute() {
television.playCctv2();
}
}
经瓷。。洞难。舆吮。。队贱。色冀。。
//播放cctv6的命令
public class CCTV1Command extends Command {
@Override
void execute() {
television.playCctv6();
}
}
抽象類Command的幾個(gè)子類實(shí)現(xiàn)也很簡(jiǎn)單柱嫌,就是將電視機(jī)TeleVision對(duì)象的playXxTV方法分布于不同的命令對(duì)象中锋恬,且因?yàn)椴煌拿顚?duì)象擁有共同的抽象類,我們很容易將這些名利功能放入一個(gè)數(shù)據(jù)結(jié)構(gòu)(比如數(shù)組)中來存儲(chǔ)執(zhí)行過的命令编丘。
命令對(duì)象設(shè)計(jì)好了与学,那么就引入命令的調(diào)用著Invoker對(duì)象了,在此例子中電視遙控器TeleController就是扮演的這個(gè)角色:
public class TeleController {
//播放記錄
List<Command> historyCommand = new ArrayList<Command>();
//切換衛(wèi)視
public void switchCommand(Command command) {
historyCommand.add(command);
command.execute();
}
//遙控器返回命令
public void back() {
if (historyCommand.isEmpty()) {
return;
}
int size = historyCommand.size();
int preIndex = size-2<=0?0:size-2;
//獲取上一個(gè)播放某衛(wèi)視的命令
Command preCommand = historyCommand.remove(preIndex);
preCommand.execute();
}
}
很簡(jiǎn)答嘉抓,遙控器對(duì)象持有一個(gè)命令集合索守,用來記錄已經(jīng)執(zhí)行過的命令。新的命令對(duì)像作為switchCommand參數(shù)來添加到集合中抑片,注意在這里就體現(xiàn)出了讓上文所術(shù)的請(qǐng)求參數(shù)化的目的卵佛。且遙控器類也提供了back方法用來模擬真實(shí)遙控器的返回功能:
所以main方法的實(shí)現(xiàn)如下:
//創(chuàng)建一個(gè)電視機(jī)
Television tv = new Television();
//創(chuàng)建一個(gè)遙控器
TeleController teleController = new TeleController();
teleController.switchCommand(new CCTV1Command(tv));
teleController.switchCommand(new CCTV2Command(tv));
teleController.switchCommand(new CCTV4Command(tv));
teleController.switchCommand(new CCTV3Command(tv));
teleController.switchCommand(new CCTV5Command(tv));
teleController.switchCommand(new CCTV1Command(tv));
teleController.switchCommand(new CCTV6Command(tv));
System.out.println("------返回上一個(gè)衛(wèi)視--------");
//模擬遙控器返回鍵
teleController.back();
teleController.back();
執(zhí)行結(jié)果如下:
--CCTV1--
--CCTV2--
--CCTV4--
--CCTV3--
--CCTV5--
--CCTV1--
--CCTV6--
----------返回上一個(gè)衛(wèi)視-------------
--CCTV1--
--CCTV5--
從上面的例子我們可以看出,命令模式的主要特點(diǎn)就是將請(qǐng)求封裝成一個(gè)個(gè)命令敞斋,以命令為參數(shù)來進(jìn)行切換级遭,達(dá)到請(qǐng)求參數(shù)化的目的,且還能通過集合這個(gè)數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)已經(jīng)執(zhí)行的請(qǐng)求渺尘,進(jìn)行回退操作。而且如果需要添加新的電視頻道说敏,只需要添加新的命令類即可
而非命令模式中鸥跟,看電視的人和電視耦合在一起;而新的命令模式則使用一個(gè)遙控器就將人和電視機(jī)解耦∫阶桑總讓你抱著電視機(jī)你也不樂意不是枫匾?
結(jié)合上述例子,最后用一個(gè)圖來簡(jiǎn)單的表示命令模式拟淮。博主喜歡“斗圖".
好了干茉,我對(duì)命令模式的理解到這里就結(jié)束了,如果大家發(fā)現(xiàn)有什么錯(cuò)誤的地方很泊,希望能不吝指正角虫。至于命令模式的缺點(diǎn),如果理解了該模式委造,他的缺點(diǎn)不是顯而易見的嗎戳鹅?博主偷個(gè)懶就不再贅述。