1. 程式人生 > >Android設計模式(十八)-組合模式

Android設計模式(十八)-組合模式

組合模式,也稱作部分整體模式。是結構型設計模式之一。組合模式畫成圖就是資料結構中的樹結構,有一個根節點,然後有很多分支。將最頂部的根節點叫做根結構件,將有分支的節點叫做枝幹構件,將沒有分支的末端節點叫做葉子構件.

定義

將物件組合成樹形結構以表示“部分-整體”的層次結構,使得使用者對單個物件和組合物件的使用具有一致性。

使用場景

  • 想表示物件的部分-整體層次結構時。
  • 希望使用者忽略單個物件和組合物件的不同,對物件使用具有統一性時。
  • 從一個整體中能夠獨立出部分模組或功能時。

UML

安全的組合模式

這裡寫圖片描述

  • Component:抽象節點,為組合中的物件宣告介面,適當的時候實現所有類的公有介面方法的預設行為。
  • Composite:定義所有枝幹節點的行為,儲存子節點,實現相關操作。
  • Leaf:葉子節點,沒有子節點,實現相關物件的行為。

看一下這個模式的通用程式碼

抽象的節點:

public abstract class Component {
    protected String name;

    public Component(String name) {
        this.name = name;
    }
    public abstract void doSonthing();
}

枝幹節點:

public class Composite extends
Component {
private List<Component> components = new ArrayList<>(); public Composite(String name) { super(name); } @Override public void doSonthing() { System.out.println(name); if (null!=components){ for (Component c:components) { c.doSonthing(); } } } public
void addChild(Component child){ components.add(child); } public void removeChild(Component child){ components.remove(child); } public Component getChild(int index){ return components.get(index); } }

葉子節點:

public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void doSonthing() {
        System.out.println(name);
    }
}

客戶端呼叫:

public class CLient {
    public static void main(String[] args) {
        Composite root = new Composite("root");
        Composite branch1 = new Composite("branch1");
        Composite branch2 = new Composite("branch2");
        Composite branch3 = new Composite("branch3");

        Leaf leaf1 = new Leaf("leaf1");
        Leaf leaf2 = new Leaf("leaf2");
        Leaf leaf3 = new Leaf("leaf3");

        branch1.addChild(leaf1);
        branch3.addChild(leaf2);
        branch3.addChild(leaf3);

        root.addChild(branch1);
        root.addChild(branch2);
        root.addChild(branch3);

        root.doSonthing();
    }
}

輸出:
這裡寫圖片描述

我們可以發現在Client使用的時候,根本沒用到介面Component。違反了依賴倒置原則。

因為介面中沒有定義公共方法,必須使用對應搞得實現節點才能完成相應的操作,叫安全的組合模式。

透明的組合模式

所以就有一種透明的組合模式,所有的節點都包含有同樣的結構
這裡寫圖片描述

抽象的節點:

public abstract class Component {
    protected String name;

    public Component(String name) {
        this.name = name;
    }
    public abstract void doSonthing();

    public abstract void addChild(Component child);
    public abstract void removeChild(Component child);
    public abstract Component getChild(int index);
}

枝幹節點:

public class Composite extends Component {
    private List<Component> components = new ArrayList<>();
    public Composite(String name) {
        super(name);
    }

    @Override
    public void doSonthing() {
        System.out.println(name);
        if (null!=components){
            for (Component c:components) {
                c.doSonthing();
            }
        }
    }

    public void addChild(Component child){
        components.add(child);
    }
    public void removeChild(Component child){
        components.remove(child);
    }
    public Component getChild(int index){
        return components.get(index);
    }

}

葉子節點:

public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void doSonthing() {
        System.out.println(name);
    }

    @Override
    public void addChild(Component child) {
        throw new UnsupportedOperationException("葉子節點沒有子節點");
    }

    @Override
    public void removeChild(Component child) {
        throw new UnsupportedOperationException("葉子節點沒有子節點");
    }

    @Override
    public Component getChild(int index) {
        throw new UnsupportedOperationException("葉子節點沒有子節點");
    }
}

客戶端呼叫:

public class CLient {
    public static void main(String[] args) {
        Component root = new Composite("root");
        Component branch1 = new Composite("branch1");
        Component branch2 = new Composite("branch2");
        Component branch3 = new Composite("branch3");

        Component leaf1 = new Leaf("leaf1");
        Component leaf2 = new Leaf("leaf2");
        Component leaf3 = new Leaf("leaf3");

        branch1.addChild(leaf1);
        branch3.addChild(leaf2);
        branch3.addChild(leaf3);

        root.addChild(branch1);
        root.addChild(branch2);
        root.addChild(branch3);

        root.doSonthing();
    }
}

輸出:
這裡寫圖片描述

簡單實現

以資料夾系統舉個例子:
這裡寫圖片描述

抽象的檔案系統:

public abstract class Dir {
    protected List<Dir> dirs = new ArrayList<>();
    private String name;

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

    public abstract void addDir(Dir dir);
    public abstract void rmDir(Dir dir);//刪除檔案或資料夾
    public abstract void clear();//清空所有元素
    public abstract void print();//列印資料夾系統結構
    public abstract List<Dir> getFiles();
    public  String getName(){
        return name;
    }
}

資料夾:

public class Folder extends Dir {
    public Folder(String name) {
        super(name);
    }

    @Override
    public void addDir(Dir dir) {
        dirs.add(dir);
    }

    @Override
    public void rmDir(Dir dir) {
        dirs.remove(dir);
    }

    @Override
    public void clear() {
        dirs.clear();
    }

    @Override
    public void print() {
        //利用遞迴來輸出資料夾結構
        System.out.print(getName()+"(");
        Iterator<Dir> i = dirs.iterator();
        while (i.hasNext()){
            Dir dir = i.next();
            dir.print();
            if (i.hasNext()){
                System.out.print(", ");
            }
        }
        System.out.print(")");
    }

    @Override
    public List<Dir> getFiles() {
        return dirs;
    }
}

檔案:

public class File extends Dir {
    public File(String name) {
        super(name);
    }

    @Override
    public void addDir(Dir dir) {
        throw new UnsupportedOperationException("檔案不支援此操作");
    }

    @Override
    public void rmDir(Dir dir) {
        throw new UnsupportedOperationException("檔案不支援此操作");
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException("檔案不支援此操作");
    }

    @Override
    public void print() {
        System.out.print(getName());
    }

    @Override
    public List<Dir> getFiles() {
        throw new UnsupportedOperationException("檔案不支援此操作");
    }
}

客戶端呼叫:

public class Client {
    public static void main(String[] args) {
        //建立根目錄 root
        Dir root = new Folder("root");
        //root下有個檔案log.txt和三個資料夾 system,user,lib;
        root.addDir(new File("log.txt"));
        Dir system = new Folder("system");
        system.addDir(new File("systemlog.txt"));
        root.addDir(system);
        Dir user = new Folder("user");
        user.addDir(new File("usernamelist.txt"));
        root.addDir(user);
        Dir lib = new Folder("lib");
        lib.addDir(new File("libs.txt"));
        root.addDir(lib);
        root.print();
    }
}

輸出:
這裡寫圖片描述

Android原始碼中的組合模式

組合模式在Android中太常用了,View和ViewGroup就是一種很標準的組合模式:

這裡寫圖片描述

在Android的檢視樹中,容器一定是ViewGroup,只有ViewGroup才能包含其他View和ViewGroup。View是沒有容器的。者是一種安全的組合模式。

總結

在Android開發中用到組合模式並不很多,組合模式更多的用於介面UI的架構設計上,而這部分讓開發者去實現的並不多。

優點

  • 可以清楚定義分層次的複雜物件,表示全部或部分層次,讓高層忽略層次的差異,方便對整個層次結構進行控制。
  • 高層模組可以一致的使用一個組合結構或其中的單個物件,不必掛心處理的是單個物件還是整個組合結構,簡化了高層模組的程式碼。
  • 增加新的枝幹和葉子構件都很方便,無需對現有類進行任何修改,就像增加一個自定義View一樣。
  • 將物件之間的關係形成樹形結構,便於控制。

缺點

  • 設計變得更加抽象,因此很難限制組合中的元件,因為他們都來自相同的抽象層。所以必須在執行時進行型別檢查才能實現。