這是我參與8月更文挑戰的第14天,活動詳情查看:8月更文挑戰java
「系統菜單」應該是全部ERP系統都必備的一個基礎模塊,反正我是沒見過哪一個ERP系統是沒有菜單設計的。典型的設計就是一個「樹狀結構」,數據庫表用一個parent_id
來構建整棵菜單樹。 既然「菜單設計」如此重要,那麼今天就來說講如何利用「組合模式」來優雅的設計系統菜單。數據庫
菜單分爲兩類:菜單目錄和具有URL地址可點擊的菜單,即樹枝節點和樹葉節點。分別設計Branch
類表明菜單目錄,Leaf
類表明菜單,類圖以下,很是簡單。 Branch安全
public class Branch {
private String name;
private String icon;
private List children;
// 省略全參構造函數
public void add(Branch branch) {
this.children.add(branch);
}
public void add(Leaf leaf) {
this.children.add(leaf);
}
public void print(){
System.out.println("name:" + name + ",icon:" + icon);
}
public List getChildren() {
return children;
}
}
複製代碼
Leafmarkdown
public class Leaf {
private String name;
private String icon;
private String url;
// 省略全參構造函數
// 省略print()
}
複製代碼
客戶端構建菜單樹,代碼省略。。。函數
如此,一棵菜單樹構建出來了,客戶端也能進行遍歷了,可是會發現代碼實現的特別彆扭啊,一點也不優雅。Branch
和Leaf
存在冗餘屬性和代碼啊,無論是目錄仍是菜單,name
和icon
都是有的,應該提取出Menu
抽象類,菜單只是一個抽象,我無論你是樹枝節點仍是樹葉節點,你都是菜單啊。樹枝節點能夠包含樹枝和樹葉,樹葉節點就僅僅包含自身的屬性特徵,這些區別能夠放到子類作特殊處理,可是對於客戶端來講,他們應該都是Menu
。post
從新設計一下類,優化後的類圖以下: 編寫
Menu
類,將菜單抽象出來,提取公共屬性和方法:優化
public abstract class Menu {
protected String name;
protected String icon;
// 省略全參構造函數
// 省略print()
}
複製代碼
樹枝節點Branchthis
public class Branch extends Menu {
private List<Menu> children = new ArrayList<>();
public Branch(String name, String icon) {
super(name, icon);
}
public void add(Menu menu) {
children.add(menu);
}
public List<Menu> getChildren() {
return children;
}
}
複製代碼
樹葉節點Leafurl
public class Leaf extends Menu{
private String url;
// 省略全參構造函數
// 省略print()
}
複製代碼
將對象組合成樹形結構以表示「部分-總體」的層次結構,使得用戶對單個對象和組合對象的使用具備一致性。spa
組合模式又被稱爲「部分-總體模式」,只要你須要維護「部分-總體」的關係,例如:樹形菜單,文件菜單等就能夠考慮使用組合模式。
細心的讀者可能已經發現了,客戶端在遍歷整棵樹時,須要判斷遍歷的節點是樹枝仍是樹葉,說白了,客戶端須要知道具體的實現,這實際上是對「依賴倒置原則」的破壞,不過能夠經過「透明模式」來解決。
優勢
缺點 仔細看看上面的代碼,客戶端在遍歷樹時,直接使用了實現類,這違反了「依賴倒置原則」。
上述實現採用的是「安全模式」,安全模式將樹枝和樹葉的功能區分開了,只有樹枝才能進行組合。 透明模式則是將組合使用的方法所有放到了抽象組件中,這樣無論是樹枝對象仍是樹葉對象,都具備相同的結構,這種方式須要客戶端判斷子類類型,操做不當容易致使異常。
將組合方法提取到抽象Menu
類中,默認不支持組合操做,Branch
重寫父類方法,實現組合功能,Leaf
不重寫,由於葉子節點自己確實不支持組合操做。
public abstract class Menu {
protected String name;
protected String icon;
// 省略全參構造函數
public void add(Menu menu){
// 默認不支持操做
throw new UnsupportedOperationException();
}
public List<Menu> getChildren(){
// 默認不支持操做
throw new UnsupportedOperationException();
}
}
複製代碼
這樣,無論是Branch
仍是Leaf
,結構上它們都是相同的,只是各自的實現不同而已。
組合模式在項目中隨處可見:樹形菜單、文件系統結構、XML結構等都是樹形結構,均可以優先考慮使用組合模式來實現。 組合模式用於將多個對象組合成樹形結構以表示「總體-部分」的結構層次,組合模式使得客戶端對簡單對象和複雜對象的使用具備一致性,方便了客戶端的調用。