组合模式-系统菜单的设计

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战java


1. 系统菜单的设计

「系统菜单」应该是全部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()
}
复制代码

客户端构建菜单树,代码省略。。。函数

如此,一棵菜单树构建出来了,客户端也能进行遍历了,可是会发现代码实现的特别别扭啊,一点也不优雅。BranchLeaf存在冗余属性和代码啊,无论是目录仍是菜单,nameicon都是有的,应该提取出Menu抽象类,菜单只是一个抽象,我无论你是树枝节点仍是树叶节点,你都是菜单啊。树枝节点能够包含树枝和树叶,树叶节点就仅仅包含自身的属性特征,这些区别能够放到子类作特殊处理,可是对于客户端来讲,他们应该都是Menupost

从新设计一下类,优化后的类图以下: 在这里插入图片描述 编写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()
}
复制代码

2. 组合模式的定义

将对象组合成树形结构以表示“部分-总体”的层次结构,使得用户对单个对象和组合对象的使用具备一致性。spa

在这里插入图片描述

组合模式通用类图
  • Component:抽象组件,提取公共的方法和属性。
  • Leaf:叶子节点,没有分支,遍历的最小单位。
  • Composite:树枝节点,复杂对象,组合树枝和树叶造成树状结构。

组合模式又被称为「部分-总体模式」,只要你须要维护「部分-总体」的关系,例如:树形菜单,文件菜单等就能够考虑使用组合模式。

细心的读者可能已经发现了,客户端在遍历整棵树时,须要判断遍历的节点是树枝仍是树叶,说白了,客户端须要知道具体的实现,这实际上是对「依赖倒置原则」的破坏,不过能够经过「透明模式」来解决。

3. 组合模式的优缺点

优势

  1. 高层模块调用简单,整棵树中全部的节点都是Component抽象,高层模块不关心本身处理的是简单对象仍是复杂对象,统一了客户端处理节点的方式。
  2. 屏蔽了节点实现细节对高层模块的影响,能够随时加入新的节点。
  3. 节点能够自由增长,扩展性高。

缺点 仔细看看上面的代码,客户端在遍历树时,直接使用了实现类,这违反了「依赖倒置原则」。

4. 透明的组合模式

上述实现采用的是「安全模式」,安全模式将树枝和树叶的功能区分开了,只有树枝才能进行组合。 透明模式则是将组合使用的方法所有放到了抽象组件中,这样无论是树枝对象仍是树叶对象,都具备相同的结构,这种方式须要客户端判断子类类型,操做不当容易致使异常。 在这里插入图片描述

透明的组合模式通用类图

将组合方法提取到抽象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,结构上它们都是相同的,只是各自的实现不同而已。

5. 总结

组合模式在项目中随处可见:树形菜单、文件系统结构、XML结构等都是树形结构,均可以优先考虑使用组合模式来实现。 组合模式用于将多个对象组合成树形结构以表示「总体-部分」的结构层次,组合模式使得客户端对简单对象和复杂对象的使用具备一致性,方便了客户端的调用。

相关文章
相关标签/搜索