命令模式
面向?qū)ο笾行汲瑁粚?shí)例化一個對象,然后填充該對象仇让。再由別的對象調(diào)用該對象典奉。
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進(jìn)行參數(shù)化丧叽。單個請求封裝成對象以后卫玖,我們就可以對這個請求存儲更多的信息,使請求擁有更多的能力踊淳。
將請求封裝為對象假瞬,那么可以不必為用戶的每一次提交請求都去執(zhí)行,只要當(dāng)前客戶已經(jīng)提交過請求迂尝,那么就忽略或提示消息給用戶笨触。
同時用戶可以撤銷請求(或者可不可以更改請求內(nèi)容?)
然后服務(wù)器將請求排隊雹舀,然后處理芦劣。
實(shí)現(xiàn)
那12306網(wǎng)站來說。
命令可能有很多種说榆,比如訂票命令虚吟,退票命令,改簽命令等等签财,等等亂七八糟的串慰。
如果能一起接收這些命令,那么就需要一個抽象層唱蒸,命令繼承一個基類邦鲫。這樣接收這個基類的指針就可以了。
所以命令類應(yīng)該有兩個。
UML
命令類
封裝請求和處理請求的對象庆捺。并不直接去處理這些請求古今。
所以他實(shí)際的成員函數(shù)都可以的protect,因?yàn)檫@些對象的成員函數(shù)都是有提交這些請求的對象來處理滔以。
這些類都應(yīng)該持有一個處理這些請求的對象捉腥,所以我們在基類中指定有這么一個私有成員,在初始化的時候填充你画。
然后每一個具體的命令類抵碟,都應(yīng)該有自己的一些數(shù)據(jù)成員,比如訂票的時候用戶信息坏匪,票的信息等拟逮,我們存儲在Data
這個集合,或者我們自己假設(shè)的一個類型中适滓。
//抽象層
class Commend
{
public:
virtual void doit()=0;
};
//具體的命令類
class OrderCom:public Commend
{
public:
OrderCom(Hander *);
vitrual void doit()
{
hander->exc();
}
private:
Data data;
Handler *handler;
}敦迄;
class RefundCom:public Commend
{
//實(shí)現(xiàn),同上
private:
Handler *handler;
};
命令類與具體執(zhí)行命令的類,應(yīng)該應(yīng)該解耦粒竖。也就是在做中間層颅崩。
這個中間層應(yīng)不應(yīng)該也是個虛類几于,然后具體的子類再執(zhí)行對應(yīng)的命令蕊苗?
這個就要示情況而定了。
因?yàn)檫@里已經(jīng)實(shí)現(xiàn)了解耦沿彭,如果對一個請求處理的對象可以有多個朽砰,那么就有必要做一個抽象層。
handle
類執(zhí)行具體處理任務(wù)喉刘。
class Handle
{
}
為了實(shí)現(xiàn)請求的排隊和集中處理瞧柔,我們還需要在做一個對象。用來集中管理這些命令子類睦裳。
說白了造锅,這些命令子類,只是在封裝需要處理的對象廉邑,和處理者哥蔚。它們他們并不進(jìn)行實(shí)際的處理。
這個類蛛蒙,可以繼承Commend
也可以不繼承糙箍,這個類持有一個容器他存儲這些Commend
,繼承的原因是:這個隊列類也有一個函數(shù)成員exc()
用來執(zhí)行容器里面Commend
。
class Exc
{
public:
void exc();//挨個執(zhí)行隊列里的命令
void add();
void remove();
private:
vector<Commend *> _list;
};
除去析構(gòu)牵祟,在移除Commend
時要一個個遍歷深夯,如果多的話,那就很麻煩了诺苹。
責(zé)任鏈模式
使多個對象都有機(jī)會處理請求咕晋,從而避免了請求的發(fā)送者和接受者之間的耦合關(guān)系雹拄。將這些對象鏈成一條鏈,并沿著這條鏈傳遞該請求/命令,直到有一個對象處理了該請求捡需。
簡單來講办桨,就是可以將一個對象(或者是命令模式中的一個請求),傳遞給一條“鏈”站辉,直到鏈中的某個處理該請求的對象呢撞,處理了個對象,那么就返回饰剥。
實(shí)現(xiàn)
現(xiàn)象一下殊霞,可以這樣做:
能夠傳遞
- 要是這個請求可以在一條鏈中傳遞,那么能想到的方法汰蓉,就是將處理這個請求的對象做一個抽象層绷蹲,然后每個實(shí)際的處理類繼承這個抽象層。然后一個儲存這個“鏈”的類顾孽,擁有一個容器祝钢,按照順序存儲處理類的對象。
2.節(jié)省一個“鏈”類若厚,想list
一樣(單向鏈表)拦英,將下一個處理對象存儲在本對象中,如果本對象不能處理該對象测秸,那么就調(diào)用下一個處理對象的處理函數(shù)疤估。
確保一定能夠被處理
調(diào)用處理類的成員函數(shù),先判斷是否能夠處理該對象霎冯,如果不能铃拇,那就。沈撞。慷荔。跳。缠俺。显晶。
也可以在條之前判斷一下_next
如果是一個nullptr
那么就返回false
。這樣的話晋修,可以不用使用終端的那個類吧碾。
- 采用第一種容器的方式,那么就遍歷對象墓卦,依次調(diào)用倦春。如果能夠處理就
break
。同時,在開始處理之前睁本,可以確保容器的尾元素一定能夠和醋里該對象尿庐。 - 采用第二種“單向鏈表”方式,那么需要確保最后一個處理類的成員函數(shù)一定能夠處理該請求呢堰,或者就返回一個不能處理抄瑟。
請求需要存儲在處理類中嘛?
采用處理類調(diào)用函數(shù)成員直接處理請求枉疼,那么請求可以不用存儲皮假。
如何添加下一個處理類
可以直接采用函數(shù)成員addNext()
的方式。
也可以采用直接在構(gòu)造函數(shù)中初始化骂维。
最后惹资,選取的是“單向鏈表”的方式。
UML
代碼
//處理類的基類
class AbHandler
{
public:
virtual bool exc(Req * req)=0;
private:
AbHandler *_next;
};
//處理類航闺,根據(jù)信息判斷是否可以執(zhí)行褪测。
class Exc1Handler:AbHandler
{
public:
Exc1Handler(AbHandler* handler):_next(handler);
virtual bool exc(Req * req)
{
if(/*判斷req信息和本類的其他數(shù)據(jù)成員是否能夠處理該請求*/)
{
//do
return true;
}
return _next->exc(req);
}
};
//鏈的終端潦刃,確保一定可以執(zhí)行侮措。
class EndHandler:Abhandler
{
//其他不變,構(gòu)造函數(shù)傳入?yún)?shù)為空
public:
virtual bool exc()
{
//提示信息
return false;
}
};
使用
int main()
{
EndHandler *endHandler=new EndHandler;
Exc1Handler *exc1=new Exc1Handler(endHandler);
//.....添加其他的乖杠。
Exc9Handler *exc9=new Exc9Handler(exc8);
//另外一個請求類分扎。。滑黔。
Req req;
exc9->exc(req);
}
鏈的終端先創(chuàng)建笆包,然后執(zhí)行的是鏈的開頭环揽。