本文已参与「新人创作礼」活动,一起开启掘金创作之路。
模式意图
组合模式用于将一组相似的对象当作一个单一的对象,依据树形结构组合对象,用来表示部分以及整体的层次。
编辑
比如文件系统,目录包含目录或者文件,目录和文件是两个对象,但这种树形结构将各个结点看作是一个对象更好操作,如遍历等,如何将目录和文件看作是同一对象来操作?
模式结构
抽象跟结点:定义系统各层次对象的共有方法和属性
树枝结点:存储叶子结点,并且和叶子结点共同组成树形结构,如文件夹对象
叶子结点:叶子结点对象,再无分支,如文件对象
文件系统案例
编辑
有三部分结构
MenuComponent
抽象类,由Menu和Menuitem继承,负责将不同类对象相同对待,并且定义二者的相同方法和属性来提高代码的复用。
每个文件或者文件夹都有名称和等级,并且有添加(add),移除(remove),获取(getChild)子菜单的方法和打印(print)菜单或者文件信息的方法。
Menu
菜单类,指文件夹,每个文件夹中有个集合,来存储文件夹或者文件
Menuitem
菜单项类,指文件
编辑
package mode; import java.util.ArrayList; import java.util.List; public class Client{ public static void main(String[] args) { //创建菜单树 MenuComponent menu1 = new Menu("菜单管理",2); //将一级菜单添加到二级菜单中 menu1.add(new MenuItem("页面访问",3)); menu1.add(new MenuItem("展开菜单",3)); menu1.add(new MenuItem("编辑菜单",3)); menu1.add(new MenuItem("删除菜单",3)); menu1.add(new MenuItem("新增菜单",3)); MenuComponent menu2 = new Menu("权限管理",2); //将一级菜单添加到二级菜单中 menu2.add(new MenuItem("页面访问",3)); menu2.add(new MenuItem("提交保存",3)); MenuComponent menu3 = new Menu("角色管理",2); //将一级菜单添加到二级菜单中 menu3.add(new MenuItem("页面访问",3)); menu3.add(new MenuItem("新增角色",3)); menu3.add(new MenuItem("修改角色",3)); //创建一级菜单 MenuComponent component = new Menu("系统管理",1); //将二级菜单添加到一级菜单中 component.add(menu1); component.add(menu2); component.add(menu3); component.print(); } } abstract class MenuComponent{ protected String name; protected int level; //添加菜单 public void add(MenuComponent menuComponent){ //只有文件夹可以添加菜单,文件不可以,避免传入文件对象进来,抛出一个异常 throw new UnsupportedOperationException(); } //移除子菜单 public void remove(MenuComponent menuComponent){ //菜单项不再包含其他内容,所以没有子菜单 throw new UnsupportedOperationException(); } //获取指定子菜单 public MenuComponent getChild(int index){ //对于菜单可以使用此方法,对于菜单项不能使用 throw new UnsupportedOperationException(); } //获取菜单或者菜单项的名称,提高代码复用性,在子类中就不需要定义了 public String getName(){ return name; } //打印菜单名称(包含子菜单和子菜单项) public abstract void print(); } class Menu extends MenuComponent{ private List<MenuComponent> menuComponentList = new ArrayList<>(); public Menu(String name,int level){ this.name = name; this.level = level; } @Override public void add(MenuComponent menuComponent) { menuComponentList.add(menuComponent); } @Override public void remove(MenuComponent menuComponent) { menuComponentList.remove(menuComponent); } @Override public MenuComponent getChild(int index) { return menuComponentList.get(index); } @Override public void print() { //打印菜单名称 for(int i = 0; i < level; i++){ System.out.print("-"); } System.out.println(name); //打印子菜单或子菜单项名称 for (MenuComponent menuComponent : menuComponentList) { menuComponent.print(); } } } class MenuItem extends MenuComponent{ public MenuItem(String name,int level){ this.name = name; this.level = level; } @Override public void print() { //打印菜单项名称 for(int i = 0; i < level; i++){ System.out.print("-"); } System.out.println(name); } }
在Client先创建一级文件,一级文件的文件集合中添加多个二级菜单,二级菜单中添加多个文件。
通过一级菜单对象打印整体结构。文件夹的print方法先打印自身,再打印集合,文件的print方法只打印自身
为什么抛出异常
上例中为什么文件中很多方法用不到还要在抽象类中定义,就是为了操作不同对象时可以调用相同接口,文件夹类调用add,remove方法会调用该类的重写方法,而文件类调用时,会调用父类中的,会抛出错误。
优点
编辑
使用场景
组合结构应树形结构而生,多用于出现树型结构的场景