組合模式
?什么是組合模式?組合模式是將一組對象組合為可像單個對象一樣被使用的結(jié)構(gòu)危队。同樣地聪建,舉個栗子來說明。
假設(shè)現(xiàn)在某國突然想教訓(xùn)一下不斷念想某魚島的某本交掏,于是開始招賢納士妆偏,招兵買馬。打戰(zhàn)吧盅弛,肯定要有士兵钱骂,士兵肯定有攻擊力,而且不同兵種攻擊力肯定不同挪鹏。假設(shè)現(xiàn)在我們有一個soldier抽象類见秽,類的定義如下:
有兩個兵種,一個是步兵infantryman讨盒,一個是炮兵artilleryman解取,返回值的大小表示攻擊力的大小,定義如下:
有了這兩個基本兵種返顺,我們就可以開始組建軍隊(duì)了禀苦。首先,這個軍隊(duì)即可以包含自己招募來的炮兵或者步兵遂鹊,也可以包含從別的軍隊(duì)抽調(diào)過來的部隊(duì)振乏,于是分別設(shè)計了兩個對象數(shù)組來分別保存士兵對象和軍隊(duì)對象,同時也提供了添加士兵對象和軍隊(duì)對象的方法秉扑,還有移除對象的方法(沒有寫出)慧邮,軍隊(duì)總戰(zhàn)斗力就是所有作戰(zhàn)單位的攻擊力的總和,所以只需要遍歷這些作戰(zhàn)單位對象并把攻擊力數(shù)值進(jìn)行疊加即可舟陆。具體代碼如下:
由于某本是個島國误澳,我們會需要運(yùn)兵船輸送兵力,此刻可以新建一個troopCarrier類秦躯,類中可以包含特定數(shù)目的步兵或者炮兵忆谓,寫法和Army類差不多。通常我們稱army對象和troopCarrier對象為組合對象踱承,稱infantryman對象和artilleryman對象為局部對象陪毡∶啄福可以看出組合對象中需要包含添加和刪除子對象的方法,而局部對象不用毡琉,但兩者共同需要實(shí)現(xiàn)的操作是aggressivity(共同的操作集)铁瞒。
現(xiàn)在我們知道了,我們的火力輸出形式主要有單體士兵輸出和整個軍隊(duì)單位的輸出桅滋,在這里為了統(tǒng)一慧耍,我們把前文的抽象類soldier改為unit,意指作戰(zhàn)單位丐谋。
一般的組合模式會在抽象類中添加add和remove方法芍碧,所以現(xiàn)在我們的抽象基類為:
可是問題又來了,為了確保接口的統(tǒng)一号俐,組合類和局部類都繼承了這個抽象基類泌豆,但是事實(shí)上局部類并不需要實(shí)現(xiàn)這個方法,明擺著嘛吏饿,軍隊(duì)可以添加成員踪危,士兵就不可以了,除非懷孕了猪落。贞远。。笨忌。所以在不小心調(diào)用了士兵單位的Add和remove方法的時候蓝仲,應(yīng)該拋出一個異常來提示我們。這時候我們只需要在抽象基類中添加默認(rèn)實(shí)現(xiàn)就行了(具體代碼省略)官疲。
下面是改寫后的army類:
class army extends unit{
? ? ? ? ? ? ? ? private $unit=array();
? ? ? ? ? ? ? ?function addUnit(unit $unit){
? ? ? ? ? ? ? ? ? ? ? if (in_array($unit,$this->unit)) {
? ? ? ? ? ? ? ? ? ? ? ? ? ?return;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? array_push($this->unit, $unit);
? ? ? ? ? ? ?}
? ? ? ? ? ? function removeUnit(unit $unit){
? ? ? ? ? ? ? ? ? ?$temp=array();
? ? ? ? ? ? ? ? ? foreach ($this->unit as $value) {
? ? ? ? ? ? ? ? ? if($unit !== $value){
? ? ? ? ? ? ? ? ? ? ? $temp[]=$value;
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? $this->unit=$temp;
? ? ? ? ? ? }
? ? ? ? ? ? ? function aggressivity(){
? ? ? ? ? ? ? ? ? ? ? ? ? $res=0;
? ? ? ? ? ? ? ? ? ?foreach ($this->unit as $value) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?$res+=$value->aggressivity();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? return $res;
? ? ? ? ? ?}
}
如果現(xiàn)在我們又需要新的兵種如傘兵袱结,然后同時需要添加一個空降部隊(duì),需要怎么做途凫?對的垢夹,很簡單,我們只需要再新增一個傘兵局部類和空降部隊(duì)組合類即可颖榜,無需大范圍修改原來的代碼棚饵。讓我們從客戶端代碼角度來感受一下煤裙。
//某國戰(zhàn)隊(duì)
$chinese_army=new army();
//傘兵對象
$s1=new infantryman();
//炮兵對象
$s2=new artilleryman();
//傘兵對象
$s3=new paraboy();
//增加一艘運(yùn)兵船
$troopcarrier1=new troopCarrier();
$troopcarrier1->addUnit($s1);
//增加空降部隊(duì)
$air=new paratroops();
$air->addUnit($s3);
//將運(yùn)兵船和空降部隊(duì)添加到中國戰(zhàn)隊(duì)
$chinese_army->addUnit($troopcarrier1);
$chinese_army->addUnit($air);
當(dāng)我們需要得知某國戰(zhàn)隊(duì)的戰(zhàn)斗力時掩完,只需要簡單地調(diào)用$chinese_army->aggressivity()便可將復(fù)雜的操作隱藏。
看完了組合模式的代碼硼砰,也許有人有疑問了且蓬,既然局部類不需要addUnit和removeUnit方法,為什么我們還要添加呢题翰?這樣做不是顯得很冗余嗎恶阴?
為了解決這個問題诈胜,我們很自然就會想到需要對這兩種不同的對象進(jìn)行區(qū)分,組合類就應(yīng)當(dāng)實(shí)現(xiàn)addUnit和removeUnit方法冯事,而局部類就不需要去實(shí)現(xiàn)焦匈。為此,我們將這兩個操作子對象的方法降到下一級對象中昵仅,將組合類放在CompositeUnit抽象類下缓熟。
//unit抽象基類
abstract class unit{
//組合對象和局部對象的區(qū)別方法
? ? ? ? ? ?function getComposite(){
? ? ? ? ? return null;
? ? ? ? ? ?}
//共同操作集
? ? ? ? ?abstract function aggressivity();
}
//CompositeUnit抽象類,將組合類的特征抽象出來
abstract class CompositeUnit extends unit{
//作戰(zhàn)單位列表
? ? ? ? ? ? ? ? ? ? private $units=array();
? ? ? ? ? ? ? ? ? ? function getComposite(){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?return $this;
? ? ? ? ? ? ? ? ? ? }
//獲取作戰(zhàn)單位列表
? ? ? ? ? ? ? ? ? ?protected function units(){
? ? ? ? ? ? ? ? ? ? ? ? ? ? return $this->units;
? ? ? ? ? ? ? ? ? ? ?}
//添加作戰(zhàn)單位
? ? ? ? ? ? ? ? ? function addUnit(unit $unit){
? ? ? ? ? ? ? ? ? ? ? ? ? ? if (in_array($unit,$this->unit)) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ?array_push($this->unit, $unit);
? ? ? ? ? ? ? ? ? ? }
//移除作戰(zhàn)單位
? ? ? ? ? ? ? ? ?function removeUnit(unit $unit){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?$temp=array();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? foreach ($this->unit as $value) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if($unit !== $value){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?$temp[]=$value;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?$this->unit=$temp;
? ? ? ? ? ? ? ? }
}
組合類army:
所以摔笤,現(xiàn)在够滑,當(dāng)我們需要添加對象的時候,首先需要調(diào)用getComposite()方法判斷對象是組合對象還是局部對象吕世。
組合模式的問題:
1.當(dāng)特殊的對象越來越多彰触,組合模式開始 顯得弊大于利,因?yàn)樾枰诿總€特殊對象類中對特殊條件進(jìn)行判斷而無法自動化來強(qiáng)制執(zhí)行規(guī)則命辖,所以在大部分局部對象可互換的情況下况毅,組合模式才最適用。
2.當(dāng)對象樹中有大量的子army對象吮龄,一個簡單的調(diào)用可能會導(dǎo)致系統(tǒng)崩潰俭茧,為了降低組合操作的成本,可以在父級對象中緩沖計算結(jié)果漓帚,使接下來的調(diào)用減少系統(tǒng)開銷母债。
3.組合模式使數(shù)據(jù)保存在關(guān)系型數(shù)據(jù)庫中并不輕松,但卻非常適合持久化XML尝抖。