設(shè)計(jì)模式--組合模式

目錄

本文的結(jié)構(gòu)如下:

  • 引言
  • 什么是組合模式
  • 模式的結(jié)構(gòu)
  • 典型代碼
  • 代碼示例
  • 優(yōu)點(diǎn)和缺點(diǎn)
  • 適用環(huán)境
  • 模式應(yīng)用

一、引言

樹(shù)形結(jié)構(gòu)是很常見(jiàn)的,比如目錄系統(tǒng),隨便點(diǎn)開(kāi)一個(gè)文件夾,文件夾下面可能有文件,也有子文件夾,子文件夾中還有子子文件夾和文件......

20171127_composite01.png

還有導(dǎo)航中的菜單。

20171127_composite02.png

還有公司的部門構(gòu)造等,展開(kāi)來(lái)看都是樹(shù)形的結(jié)構(gòu)。

這些樹(shù)形結(jié)構(gòu)在面向?qū)ο蟮氖澜缰幸话闶怯?strong>組合模式來(lái)處理的。

組合模式通過(guò)一種巧妙的設(shè)計(jì)方案,可以一致性地處理整個(gè)樹(shù)形結(jié)構(gòu)或者樹(shù)形結(jié)構(gòu)的一部分,也可以一致性地處理樹(shù)形結(jié)構(gòu)中的葉子節(jié)點(diǎn)(不包含子節(jié)點(diǎn)的節(jié)點(diǎn))和容器節(jié)點(diǎn)(包含子節(jié)點(diǎn)的節(jié)點(diǎn))。

二、什么是組合模式

對(duì)于樹(shù)形結(jié)構(gòu),當(dāng)容器對(duì)象(如文件夾)的某一個(gè)方法被調(diào)用時(shí),將遍歷整個(gè)樹(shù)形結(jié)構(gòu),尋找也包含這個(gè)方法的成員對(duì)象(可以是容器對(duì)象,也可以是葉子對(duì)象)并調(diào)用執(zhí)行。這是靠遞歸調(diào)用的機(jī)制實(shí)現(xiàn)的。

由于容器對(duì)象和葉子對(duì)象在功能上的區(qū)別,在使用這些對(duì)象的代碼中必須有區(qū)別地對(duì)待容器對(duì)象和葉子對(duì)象,而實(shí)際上大多數(shù)情況下我們希望一致地處理它們,因?yàn)閷?duì)于這些對(duì)象的區(qū)別對(duì)待將會(huì)使得程序非常復(fù)雜。組合模式為解決此類問(wèn)題而誕生,它可以讓葉子對(duì)象和容器對(duì)象的使用具有一致性。

組合模式定義如下:

組合模式(Composite Pattern):組合多個(gè)對(duì)象形成樹(shù)形結(jié)構(gòu)以表示具有“整體—部分”關(guān)系的層次結(jié)構(gòu)。組合模式對(duì)單個(gè)對(duì)象(即葉子對(duì)象)和組合對(duì)象(即容器對(duì)象)的使用具有一致性,組合模式又可以稱為“整體—部分”(Part-Whole)模式,它是一種對(duì)象結(jié)構(gòu)型模式。

三、模式的結(jié)構(gòu)

組合模式的UML類圖如下:

20171127_composite03.png

在組合模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:

  • Component(抽象構(gòu)件):它可以是接口或抽象類,為葉子構(gòu)件和容器構(gòu)件對(duì)象聲明接口,在該角色中可以包含所有子類共有行為的聲明和實(shí)現(xiàn)。在抽象構(gòu)件中定義了訪問(wèn)及管理它的子構(gòu)件的方法,如增加子構(gòu)件、刪除子構(gòu)件、獲取子構(gòu)件等。
  • Leaf(葉子構(gòu)件):它在組合結(jié)構(gòu)中表示葉子節(jié)點(diǎn)對(duì)象,葉子節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn),它實(shí)現(xiàn)了在抽象構(gòu)件中定義的行為。對(duì)于那些訪問(wèn)及管理子構(gòu)件的方法,可以通過(guò)異常等方式進(jìn)行處理。
  • Composite(容器構(gòu)件):它在組合結(jié)構(gòu)中表示容器節(jié)點(diǎn)對(duì)象,容器節(jié)點(diǎn)包含子節(jié)點(diǎn),其子節(jié)點(diǎn)可以是葉子節(jié)點(diǎn),也可以是容器節(jié)點(diǎn),它提供一個(gè)集合用于存儲(chǔ)子節(jié)點(diǎn),實(shí)現(xiàn)了在抽象構(gòu)件中定義的行為,包括那些訪問(wèn)及管理子構(gòu)件的方法,在其業(yè)務(wù)方法中可以遞歸調(diào)用其子節(jié)點(diǎn)的業(yè)務(wù)方法。

組合模式的關(guān)鍵是定義了一個(gè)抽象構(gòu)件類,它既可以代表葉子,又可以代表容器,而客戶端針對(duì)該抽象構(gòu)件類進(jìn)行編程,無(wú)須知道它到底表示的是葉子還是容器,可以對(duì)其進(jìn)行統(tǒng)一處理。同時(shí)容器對(duì)象與抽象構(gòu)件類之間還建立一個(gè)聚合關(guān)聯(lián)關(guān)系,在容器對(duì)象中既可以包含葉子,也可以包含容器,以此實(shí)現(xiàn)遞歸組合,形成一個(gè)樹(shù)形結(jié)構(gòu)。

如果不使用組合模式,客戶端代碼將過(guò)多地依賴于容器對(duì)象復(fù)雜的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu),容器對(duì)象內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)的變化將引起客戶代碼的頻繁變化,帶來(lái)了代碼維護(hù)復(fù)雜、可擴(kuò)展性差等弊端。組合模式的引入將在一定程度上解決這些問(wèn)題。

四、典型代碼

4.1、抽象構(gòu)件角色

一般將抽象構(gòu)件類設(shè)計(jì)為接口或抽象類,將所有子類共有方法的聲明和實(shí)現(xiàn)放在抽象構(gòu)件類中。對(duì)于客戶端而言,將針對(duì)抽象構(gòu)件編程,而無(wú)須關(guān)心其具體子類是容器構(gòu)件還是葉子構(gòu)件。

public abstract class Component {
    /**
     * 增加成員
     * @param c
     */
    public void add(Component c){
        throw new UnsupportedOperationException();
    }

    /**
     * 刪除成員
     * @param c
     */
    public void remove(Component c){
        throw new UnsupportedOperationException();
    }

    /**
     * 獲取成員
     * @param i
     * @return
     */
    public Component getChild(int i){
        throw new UnsupportedOperationException();
    }

    /**
     * 業(yè)務(wù)方法
     */
    public void operation(){
        throw new UnsupportedOperationException();
    }
}

為什么所有方法都拋出UnsupportedOperationException?是因?yàn)橛行┓椒ㄖ粚?duì)容器構(gòu)件有意義,而有些方法只對(duì)葉子構(gòu)件有意義,這樣,如果某個(gè)子構(gòu)件不支持某個(gè)操作,直接繼承默認(rèn)方法就可以了。

4.2、葉子構(gòu)件

葉子構(gòu)件繼承自抽象構(gòu)建抽象構(gòu)建:

public class Leaf extends Component {
    @Override
    public void operation(){
        System.out.println("子構(gòu)件");
    }
}

葉子構(gòu)件不能再包含子構(gòu)件,因此在葉子構(gòu)件中只需事先業(yè)務(wù)方法,其他默認(rèn)繼承,拋出為什么所有方法都拋出UnsupportedOperationException。

4.3、容器構(gòu)件

public class Composite extends Component {
    private List<Component> list = new ArrayList<Component>();

    @Override
    public void add(Component c){
        list.add(c);
    }

    @Override
    public void remove(Component c) {
        list.remove(c);
    }

    @Override
    public Component getChild(int i) {
        return list.get(i);
    }

    @Override
    public void operation(){
        for (Component child: list){
            child.operation();
        }
    }
}

容器構(gòu)件中實(shí)現(xiàn)了在抽象構(gòu)件中聲明的所有方法,既包括業(yè)務(wù)方法,也包括用于訪問(wèn)和管理成員子構(gòu)件的方法。

需要注意的是在實(shí)現(xiàn)具體業(yè)務(wù)方法時(shí),由于容器構(gòu)件充當(dāng)?shù)氖侨萜鹘巧蓡T構(gòu)件,因此它將調(diào)用其成員構(gòu)件的業(yè)務(wù)方法。在組合模式結(jié)構(gòu)中,由于容器構(gòu)件中仍然可以包含容器構(gòu)件,因此在對(duì)容器構(gòu)件進(jìn)行處理時(shí)需要使用遞歸算法,即在容器構(gòu)件的operation()方法中遞歸調(diào)用其成員構(gòu)件的operation()方法。

五、代碼示例

假設(shè)這樣的場(chǎng)景:

在電腦E盤有個(gè)文件夾,該文件夾下面有很多文件,有視頻文件,有音頻文件,有圖像文件,還有包含視頻、音頻及圖像的文件夾,十分雜亂,現(xiàn)希望將這些雜亂的文件展示出來(lái)。

20171127_composite04.png

這里其實(shí)就是一個(gè)樹(shù)形結(jié)構(gòu),根據(jù)一定的規(guī)則分類后,大致是這樣的:

20171127_composite05.png

5.1、不使用組合模式

注:當(dāng)然可以一個(gè)循環(huán)遍歷就搞定了,因?yàn)檫@里用的是文件的形式,如果是別的形式呢?所以不要太較真了,只是舉例。

public class MusicFile {
    private String name;

    public MusicFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class VideoFile {
    private String name;

    public VideoFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class ImageFile {
    private String name;

    public ImageFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class Folder {
    private String name;
    //音樂(lè)
    private List<MusicFile> musicList = new ArrayList<MusicFile>();
    //視頻
    private List<VideoFile> videoList = new ArrayList<VideoFile>();
    //圖片
    private List<ImageFile> imageList = new ArrayList<ImageFile>();
    //文件夾
    private List<Folder> folderList = new ArrayList<Folder>();

    public Folder(String name){
        this.name = name;
    }

    public void addFolder(Folder folder){
        folderList.add(folder);
    }

    public void addImage(ImageFile image){
        imageList.add(image);
    }

    public void addVideo(VideoFile video){
        videoList.add(video);
    }

    public void addMusic(MusicFile music){
        musicList.add(music);
    }

    public void print(){
        for (MusicFile music : musicList){
            music.print();
        }
        for (VideoFile video : videoList){
            video.print();
        }
        for(ImageFile image : imageList){
            image.print();
        }
        for (Folder folder : folderList){
            folder.print();
        }
    }
}

客戶端測(cè)試:

public class Client {
    public static void main(String[] args) {
        MusicFile m1 = new MusicFile("盡頭.mp3");
        MusicFile m2 = new MusicFile("飄洋過(guò)海來(lái)看你.mp3");
        MusicFile m3 = new MusicFile("曾經(jīng)的你.mp3");
        MusicFile m4 = new MusicFile("take me to your heart.mp3");

        VideoFile v1 = new VideoFile("戰(zhàn)狼2.mp4");
        VideoFile v2 = new VideoFile("理想.avi");
        VideoFile v3 = new VideoFile("瑯琊榜.avi");

        ImageFile i1 = new ImageFile("敦煌.png");
        ImageFile i2 = new ImageFile("baby.jpg");
        ImageFile i3 = new ImageFile("girl.jpg");

        Folder aa = new Folder("aa");
        aa.addImage(i3);

        Folder bb = new Folder("bb");
        bb.addMusic(m4);
        bb.addVideo(v3);

        Folder top = new Folder("top");
        top.addFolder(aa);
        top.addFolder(bb);
        top.addMusic(m1);
        top.addMusic(m2);
        top.addMusic(m3);
        top.addVideo(v1);
        top.addVideo(v2);
        top.addImage(i1);
        top.addImage(i2);

        top.print();
    }
}

如果采用上述的形式,有幾個(gè)缺點(diǎn):

  • 文件夾類Folder的設(shè)計(jì)和實(shí)現(xiàn)都非常復(fù)雜,需要定義多個(gè)集合存儲(chǔ)不同類型的成員,而且需要針對(duì)不同的成員提供增加、刪除和獲取等管理和訪問(wèn)成員的方法,存在大量的冗余代碼,系統(tǒng)維護(hù)較為困難;
  • 由于系統(tǒng)沒(méi)有提供抽象層,客戶端代碼必須有區(qū)別地對(duì)待充當(dāng)容器的文件夾Folder和充當(dāng)葉子的MusicFile、ImageFile和VideoFile,無(wú)法統(tǒng)一對(duì)它們進(jìn)行處理;
  • 系統(tǒng)的靈活性和可擴(kuò)展性差,如果增加了新的類型的葉子和容器都需要對(duì)原有代碼進(jìn)行修改,例如如果需要在系統(tǒng)中增加一種新類型的文本文件TextFile,則必須修改Folder類的源代碼,否則無(wú)法在文件夾中添加文本文件。

5.2、使用組合模式改進(jìn)

為了讓系統(tǒng)具有更好的靈活性和可擴(kuò)展性,客戶端可以一致地對(duì)待文件和文件夾,定義一個(gè)抽象構(gòu)件AbstractFile,F(xiàn)older充當(dāng)容器構(gòu)件,MusicFile、VideoFile和ImageFile充當(dāng)葉子構(gòu)件。

20171127_composite06.png

抽象構(gòu)件AbstractFile:

public abstract class AbstractFile {
    public void add(AbstractFile file){
        throw new UnsupportedOperationException();
    }
    
    public void remove(AbstractFile file){
        throw new UnsupportedOperationException();
    }

    public AbstractFile getChild(int i){
        throw new UnsupportedOperationException();
    }
    
    public void print(){
        throw new UnsupportedOperationException();
    }
}

葉子構(gòu)件:

public class MusicFile extends AbstractFile{
    private String name;

    public MusicFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class VideoFile extends AbstractFile{
    private String name;

    public VideoFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

public class ImageFile extends AbstractFile{
    private String name;

    public ImageFile(String name){
        this.name = name;
    }

    public void print(){
        System.out.println(name);
    }
}

容器構(gòu)件:

public class Folder extends AbstractFile{
    private String name;
    private List<AbstractFile> files = new ArrayList<AbstractFile>();

    public Folder(String name){
        this.name = name;
    }

    @Override
    public void add(AbstractFile file){
        files.add(file);
    }

    @Override
    public void remove(AbstractFile file){
        files.remove(file);
    }

    @Override
    public AbstractFile getChild(int i){
        return files.get(i);
    }

    @Override
    public void print(){
        for (AbstractFile file : files){
            file.print();
        }
    }
}

客戶端測(cè)試:

public class Client {
    public static void main(String[] args) {
        AbstractFile m1 = new MusicFile("盡頭.mp3");
        AbstractFile m2 = new MusicFile("飄洋過(guò)海來(lái)看你.mp3");
        AbstractFile m3 = new MusicFile("曾經(jīng)的你.mp3");
        AbstractFile m4 = new MusicFile("take me to your heart.mp3");

        AbstractFile v1 = new VideoFile("戰(zhàn)狼2.mp4");
        AbstractFile v2 = new VideoFile("理想.avi");
        AbstractFile v3 = new VideoFile("瑯琊榜.avi");

        AbstractFile i1 = new ImageFile("敦煌.png");
        AbstractFile i2 = new ImageFile("baby.jpg");
        AbstractFile i3 = new ImageFile("girl.jpg");

        AbstractFile aa = new Folder("aa");
        aa.add(i3);

        AbstractFile bb = new Folder("bb");
        bb.add(m4);
        bb.add(v3);

        AbstractFile top = new Folder("top");
        top.add(aa);
        top.add(bb);
        top.add(m1);
        top.add(m2);
        top.add(m3);
        top.add(v1);
        top.add(v2);
        top.add(i1);
        top.add(i2);

        top.print();
    }
}

用組合模式提供一個(gè)抽象構(gòu)件后,客戶端可以一致對(duì)待容器構(gòu)件和葉子構(gòu)件,進(jìn)行統(tǒng)一處理,并且大量減少了冗余,擴(kuò)展性也很好,新增TextFile無(wú)需修改Folder源碼,只需修改客戶端即可。

當(dāng)然,這里似乎有點(diǎn)違法“迭代器模式”中講的“單一職責(zé)原則”,的確是,抽象構(gòu)件不但要管理層次結(jié)構(gòu),還要執(zhí)行一些業(yè)務(wù)操作。

我覺(jué)得應(yīng)該這樣理解問(wèn)題,設(shè)計(jì)模式并不應(yīng)該是生套似的,各種設(shè)計(jì)原則也并不是說(shuō)一定不能破壞的,所有的種種都是為了更好的解決問(wèn)題,更好的進(jìn)行擴(kuò)展維護(hù),當(dāng)適度破壞既定的原則,卻可以更好的解決問(wèn)題時(shí),顯然這里以單一設(shè)計(jì)原則換取了透明性,這種折中方案是可取的。

六、優(yōu)點(diǎn)和缺點(diǎn)

6.1、優(yōu)點(diǎn)

組合模式的主要優(yōu)點(diǎn)如下:

  • 組合模式可以清楚地定義分層次的復(fù)雜對(duì)象,表示對(duì)象的全部或部分層次,它讓客戶端忽略了層次的差異,方便對(duì)整個(gè)層次結(jié)構(gòu)進(jìn)行控制。
  • 客戶端可以一致地使用一個(gè)組合結(jié)構(gòu)或其中單個(gè)對(duì)象,不必關(guān)心處理的是單個(gè)對(duì)象還是整個(gè)組合結(jié)構(gòu),簡(jiǎn)化了客戶端代碼。
  • 在組合模式中增加新的容器構(gòu)件和葉子構(gòu)件都很方便,無(wú)須對(duì)現(xiàn)有類庫(kù)進(jìn)行任何修改,符合“開(kāi)閉原則”。
  • 組合模式為樹(shù)形結(jié)構(gòu)的面向?qū)ο髮?shí)現(xiàn)提供了一種靈活的解決方案,通過(guò)葉子對(duì)象和容器對(duì)象的遞歸組合,可以形成復(fù)雜的樹(shù)形結(jié)構(gòu),但對(duì)樹(shù)形結(jié)構(gòu)的控制卻非常簡(jiǎn)單。

6.2、缺點(diǎn)

組合模式的主要缺點(diǎn)如下:

  • 破壞了“單一職責(zé)原則”。
  • 在增加新構(gòu)件時(shí)很難對(duì)容器中的構(gòu)件類型進(jìn)行限制。有時(shí)候我們希望一個(gè)容器中只能有某些特定類型的對(duì)象,例如在某個(gè)文件夾中只能包含文本文件,使用組合模式時(shí),不能依賴類型系統(tǒng)來(lái)施加這些約束,因?yàn)樗鼈兌紒?lái)自于相同的抽象層,在這種情況下,必須通過(guò)在運(yùn)行時(shí)進(jìn)行類型檢查來(lái)實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)過(guò)程較為復(fù)雜。

七、適用環(huán)境

在以下情況下可以考慮使用組合模式:

  • 在具有整體和部分的層次結(jié)構(gòu)中,希望通過(guò)一種方式忽略整體與部分的差異,客戶端可以一致地對(duì)待它們。
  • 在一個(gè)使用面向?qū)ο笳Z(yǔ)言開(kāi)發(fā)的系統(tǒng)中需要處理一個(gè)樹(shù)形結(jié)構(gòu)。
  • 在一個(gè)系統(tǒng)中能夠分離出葉子對(duì)象和容器對(duì)象,而且它們的類型不固定,需要增加一些新的類型。

八、模式應(yīng)用

組合模式使用面向?qū)ο蟮乃枷雭?lái)實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)的構(gòu)建與處理,描述了如何將容器對(duì)象和葉子對(duì)象進(jìn)行遞歸組合,實(shí)現(xiàn)簡(jiǎn)單,靈活性好。由于在軟件開(kāi)發(fā)中存在大量的樹(shù)形結(jié)構(gòu),因此組合模式是一種使用頻率較高的結(jié)構(gòu)型設(shè)計(jì)模式,Java SE中的AWT和Swing包的設(shè)計(jì)就基于組合模式,在這些界面包中為用戶提供了大量的容器構(gòu)件(如Container)和成員構(gòu)件(如Checkbox、Button和TextArea等)。

20171127_composite07.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,069評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,873評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評(píng)論 6 408
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,128評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,365評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,582評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,821評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,595評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • 繼承是is-a的關(guān)系。組合和聚合有點(diǎn)像,有些書(shū)上沒(méi)有作區(qū)分,都稱之為has-a,有些書(shū)上對(duì)其進(jìn)行了較為嚴(yán)格區(qū)分,組...
    時(shí)待吾閱讀 467評(píng)論 0 1
  • 介紹 這篇主要講述設(shè)計(jì)模式中的組合模式。組合模式又叫部分整體模式,是用于把一組相似的對(duì)象當(dāng)作一個(gè)單一的對(duì)象。組合模...
    東西的南北閱讀 341評(píng)論 0 1
  • 1.組合模式的定義及使用場(chǎng)景組合模式也稱為部分整體模式,結(jié)構(gòu)型設(shè)計(jì)模式之一,組合模式比較簡(jiǎn)單,它將一組相似的對(duì)象看...
    GB_speak閱讀 870評(píng)論 0 2
  • View 和 ViewGroup 的 關(guān)系 在我們前面對(duì) 事件的分發(fā) 和 View 的分發(fā)中我們可以知道這兩者是密...
    銳_nmpoi閱讀 2,005評(píng)論 0 0
  • 原文地址:LoveDev 對(duì)于樹(shù)形結(jié)構(gòu),容器對(duì)象(如文件夾)可以進(jìn)行添加刪除葉子對(duì)象(如文件)等操作,但是葉子對(duì)象...
    KevinLive閱讀 383評(píng)論 2 1