设计模式——组合模式

一、概述

组合模式:
将对象组合成树形结构来表示“整体-部分”的层次结构

组合模式其实很简单,就类似于我们经常在SQL中简单的自连接一样,还有数据结构中的链表、二叉树,其实都可以用组合模式来解释,个人感觉官方的定义甚至还显得更加晦涩难懂了。
而且通常我们使用中并不会在意是不是叶子结点,通俗的说就是一个对象既可以代表根节点又可以代表叶子结点。

二、角色职责与UML

2.1 角色与职责

Component

  • 为组合中的对象声明接口
  • 在适当的情况下,实现所有类共有接口的缺省行为
  • 声明一个接口用于访问和管理Component的子组件

Leaf

  • 在组合中表示叶节点对象、叶节点没有子节点

Composite

  • 定义有子部件的那些部位的行为
  • 存储子部件

通常使用中并不会真的去分Leaf和Composite

2.2 UML图

在这里插入图片描述

三、示例

组合模式应用很广泛,为了通俗易懂,就以菜单来举例吧。
而且和上文中说的一样,个人觉得并不需要彻底的分清Leaf还是Composite,因为在用户的调用层面,他接触都是Compoent。

/**
 * 菜单抽象类,定义菜单的行为
 * @author ZhongJing </p>
 */
public abstract class ComponentMenu {

    /**
     * 添加一个子菜单
     */
    public abstract void addChildMenu(ComponentMenu menu);

    /**
     * 打印所有子菜单
     */
    public abstract void printMenu();

}
/**
 * 叶子菜单:没有子菜单的菜单
 * @author ZhongJing </p>
 */
public class LeafMenu extends ComponentMenu {

    private String menuName;

    @Override
    public void addChildMenu(ComponentMenu menu) {
        System.out.println("错误:该结点为叶子菜单,无法添加子菜单");
    }

    @Override
    public void printMenu() {
        System.out.println(menuName);
    }
}
/**
 * 有子菜单的结点
 * @author ZhongJing </p>
 */
public class CompositeMenu extends ComponentMenu {

    private String menuName;

    private List<ComponentMenu> menuList;

    public CompositeMenu(String menuName) {
        this.menuName = menuName;
        menuList = new ArrayList<>();
    }

    @Override
    public void addChildMenu(ComponentMenu menu) {
        menuList.add(menu);
    }

    @Override
    public void printMenu() {
        System.out.println(menuName);
        if (menuList.size() > 0) {
            for (ComponentMenu childMenu : menuList) {
                childMenu.printMenu();
            }
        }
    }
}

创建一个测试类进行测试:

public class MainTest {

    public static void main(String[] args) {
        // 创建一个非叶子菜单作为根菜单
        ComponentMenu rootMenu = new CompositeMenu("根菜单");

        // 创建一个叶子菜单作为一级菜单:叶子菜单不能添加子菜单
        ComponentMenu leafMenu1 = new LeafMenu("一级菜单 ---- 1");

        // 创建一个非叶子菜单作为一级菜单
        ComponentMenu compositeMenu1 = new CompositeMenu("一级菜单 ---- 2");

        // 创建根菜单与一级菜单的联系:添加两个一级菜单到根菜单
        rootMenu.addChildMenu(leafMenu1);
        rootMenu.addChildMenu(compositeMenu1);

        // 给叶子菜单添加一个菜单(不支持,会报错)
        leafMenu1.addChildMenu(new CompositeMenu("二级菜单 ---- 1"));

        // 给非叶子菜单添加一个菜单
        compositeMenu1.addChildMenu(new CompositeMenu("二级菜单 ---- 2"));

        // 按层次打印遍历所有菜单
        rootMenu.printMenu();
    }
}

运行结果如下:

错误:该结点为叶子菜单,无法添加子菜单
根菜单
一级菜单 ---- 1
一级菜单 ---- 2
二级菜单 ---- 2

通过结果可以看到,我们对叶子菜单进行的限制确实起到了作用,虽然调用时都是以ComponentMenu对象来调用的,但是叶子菜单确实无法添加子菜单。
其实这种结构的组合并不是那么的常用,如果没有叶子的限制,我一般使用的下面这种结构:
同样以菜单为例

/**
 * 菜单,既可以作为叶子也可以作为根
 */
public class Menu{

    private String menuName;

    private List<Menu> menuList;

    public Menu(String menuName) {
        this.menuName = menuName;
        menuList = new ArrayList<>();
    }

    /**
     * 添加一个子菜单
     */
    public void addChildMenu(Menu menu) {
        menuList.add(menu);
    }

    /**
     * 打印所有子菜单
     */
    public void printMenu() {
        System.out.println(menuName);
        for (Menu menu : menuList) {
            menu.printMenu();
        }
    }

}

测试一下:


/**
 * @author ZhongJing </p>
 */
public class MainTest2 {

    public static void main(String[] args) {

        Menu rootMenu = new Menu("根菜单");

        Menu oneMenu1 = new Menu("一级菜单1");
        Menu oneMenu2 = new Menu("一级菜单2");

        oneMenu1.addChildMenu(new Menu("二级菜单1"));
        oneMenu1.addChildMenu(new Menu("二级菜单2"));
        oneMenu2.addChildMenu(new Menu("二级菜单3"));

        rootMenu.addChildMenu(oneMenu1);
        rootMenu.addChildMenu(oneMenu2);

        rootMenu.printMenu();
    }

}

运行结果如下:

根菜单
一级菜单1
二级菜单1
二级菜单2
一级菜单2
二级菜单3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值