將對(duì)象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性处面,以便能夠讓客戶端以一致的方式處理個(gè)別對(duì)象以及組合對(duì)象。
組合模式也稱為部分整體模式,通過抽象出部分與整體的公共部分接口切厘,然后達(dá)到部分與整體在被操作使用上的一致性珊皿,用戶忽略組合對(duì)象與單個(gè)對(duì)象的不同网缝,統(tǒng)一地使用組合結(jié)構(gòu)中的所有對(duì)象。
看下組合模式的組成
- 抽象構(gòu)件角色Component:它為組合中的對(duì)象聲明接口蟋定,定義參加組合對(duì)象的共有方法和屬性粉臊,可以定義一些默認(rèn)的函數(shù)或?qū)傩浴?/li>
- 樹葉構(gòu)件角色Leaf:在組合中表示葉子節(jié)點(diǎn),實(shí)現(xiàn)抽象構(gòu)件角色聲明的接口驶兜。
- 樹枝構(gòu)件角色Composite:在組合中表示分支節(jié)點(diǎn)對(duì)象——有子節(jié)點(diǎn)扼仲,實(shí)現(xiàn)抽象構(gòu)件角色聲明的接口.它的作用是組合樹枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)形成一個(gè)樹形結(jié)構(gòu)。
安全性組合與透明性組合
組合模式中必須提供樹枝節(jié)點(diǎn)對(duì)子對(duì)象的管理方法促王,不然無法完成對(duì)子對(duì)象的添加刪除等等操作犀盟,也就失去了靈活性和擴(kuò)展性。這里就涉及到這些方法是定義在Compnent中還是定義在Compsite中蝇狼?
一種方式是在Component里面聲明所有的用來管理子類對(duì)象的方法阅畴,以達(dá)到Component接口的最大化(如下圖所示)。目的就是為了使客戶看來在接口層次上樹葉和分支沒有區(qū)別——透明性迅耘。但樹葉是不存在子類的贱枣,因此Component聲明的一些方法對(duì)于樹葉來說是不適用的。這樣也就帶來了一些安全性問題颤专。
另一種方式就是只在Composite里面聲明所有的用來管理子類對(duì)象的方法(如下圖所示)纽哥。這樣就避免了上一種方式的安全性問題,但是由于葉子和分支有不同的接口栖秕,所以又失去了透明性春塌。
《設(shè)計(jì)模式》一書認(rèn)為:在這一模式中,相對(duì)于安全性,我們比較強(qiáng)調(diào)透明性只壳。對(duì)于第一種方式中葉子節(jié)點(diǎn)內(nèi)不需要的方法可以使用空處理或者異常報(bào)告的方式來解決俏拱。
這里在看的時(shí)候也看到兩個(gè)不錯(cuò)的實(shí)例,一個(gè)是針對(duì)公司吼句、部門的實(shí)例锅必。另外一個(gè)是在Android中View和ViewGroup的例子,都是組合模式的代表惕艳。這里我想到了Dota中的一個(gè)場(chǎng)景搞隐。Dota中以英雄做核心,英雄又分為力量远搪、敏捷劣纲、智力,各個(gè)類型的英雄又有很多個(gè)實(shí)現(xiàn)谁鳍。
1味廊、抽象所有英雄的操作HeroComponent接口
public abstract class HeroCompnent {
/**
* 添加
*/
public abstract void add(HeroCompnent heroCompnent);
/**
* 移動(dòng)
*/
public abstract void move();
/**
* 攻擊
*/
public abstract void attack();
}
2、定義葉子節(jié)點(diǎn)棠耕,比如實(shí)現(xiàn)一個(gè)英雄-影魔
/**
* 影魔類
* @author Iflytek_dsw
*
*/
public class YaphetsLeaf extends HeroCompnent{
@Override
public void move() {
System.out.println("Yaphets move");
}
@Override
public void attack() {
System.out.println("Yaphets attack");
}
@Override
public void add(HeroCompnent heroCompnent) {
//這里是一個(gè)缺省的方法
}
}
3、定義智力英雄
/**
* 智力英雄
* @author Iflytek_dsw
*
*/
class IntellectualHero extends HeroCompnent{
private List<HeroCompnent> listIntell = new ArrayList<>();
public IntellectualHero(){
}
@Override
public void move() {
for(HeroCompnent tempCompnent : listIntell){
tempCompnent.move();
}
}
@Override
public void attack() {
for(HeroCompnent tempCompnent : listIntell){
tempCompnent.attack();
}
}
@Override
public void add(HeroCompnent heroCompnent) {
listIntell.add(heroCompnent);
}
}
/**
* 沉默術(shù)士
* @author Iflytek_dsw
*
*/
class SilenceHero extends IntellectualHero{
@Override
public void move() {
System.out.println("SilenceHero move");
}
@Override
public void attack() {
System.out.println("SilenceHero attack");
}
}
/**
* 食人魔法師
* @author Iflytek_dsw
*
*/
class AggronStonebreaker extends IntellectualHero{
@Override
public void move() {
System.out.println("AggronStonebreaker move");
}
@Override
public void attack() {
System.out.println("AggronStonebreaker attack");
}
}
4柠新、定義所有英雄的Compsite
/**
* 所有的英雄
* @author Iflytek_dsw
*
*/
public class HeroCompsite extends HeroCompnent {
private List<HeroCompnent> listHeroCompsite = new ArrayList<HeroCompnent>();
@Override
public void add(HeroCompnent heroCompnent) {
listHeroCompsite.add(heroCompnent);
}
@Override
public void move() {
for(HeroCompnent tempCompnent : listHeroCompsite){
tempCompnent.move();
}
}
@Override
public void attack() {
for(HeroCompnent tempCompnent : listHeroCompsite){
tempCompnent.attack();
}
}
}
定義客戶端
public class Client {
public static void main(String []args){
/**
* 首先窍荧,我們也需要明確一點(diǎn),我們把這些邏輯設(shè)計(jì)好恨憎,但是他們之間的關(guān)系蕊退、組裝還是需要單獨(dú)維護(hù)的。
* 不然如果新增一個(gè)種類憔恳,都去在Compsite中維護(hù)顯然違背了面向?qū)ο蟮脑O(shè)計(jì)原則瓤荔,改動(dòng)過大。
*/
//客戶想看看影魔的攻擊
HeroCompnent yaphetsLeaf = new YaphetsLeaf();
yaphetsLeaf.attack();
//客戶想看下沉默術(shù)士的移動(dòng)效果
HeroCompnent silence = new SilenceHero();
silence.move();
//客戶想了解下所有智力英雄的攻擊效果
IntellectualHero intellectualHero = new IntellectualHero();
intellectualHero.add(new SilenceHero());
intellectualHero.add(new AggronStonebreaker());
intellectualHero.attack();
}
}
在上面的例子中钥组,我們能看到組合模式可以很靈活的組合成我們需要的操作输硝。只需要把關(guān)系理清楚,然后組合好程梦〉惆眩客戶端只需要進(jìn)行按照需要添加元素來進(jìn)行操作,不需要關(guān)注邏輯關(guān)系屿附。
總結(jié)
在學(xué)習(xí)組合模式過程中郎逃,看了網(wǎng)上很多文章的介紹,但總是理解的不是很透徹挺份“玻總是理解“整體-部分”的概念理解不是很透徹。這個(gè)整體理解也是一個(gè)大種類,這個(gè)大類里面可以包含很多小類优训,操作這個(gè)類別的時(shí)候朵你,直接操作這個(gè)整體。
維護(hù)一個(gè)技術(shù)更新確實(shí)非常難型宙,但是看到大家的點(diǎn)贊撬呢、收藏很開心,希望大家多多支持妆兑。同時(shí)歡迎大家留言交流魂拦,共同進(jìn)步。