正文

一、定义

组合模式允许你将对象合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理组合对象以及个体对象。

  • 组合对象:包含其他组件的组件。
  • 个体对象(叶节点对象):没有包含其他组件的组件。

要点:

  • 组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点。
  • 通过将组合对象和个体对象放在树形结构中,我们创建了一个“整体/部分”层次结构。如果将整个树形结构视为一个“大组合”的话,那么这个树形结构的每一个“子树形结构”也是一个组合,包括叶节点也可以被视为一个不包含其他对象的组合。这样一来,我们就有了“以一致的方式处理”的基础了。
  • 所谓“以一致的方式处理”,是指组合和叶节点具有共同的方法可以调用。这就要求它们必须实现相同的接口。
  • 组合模式允许客户对组合对象和个体对象一视同仁。换句话说,我们可以把相同的操作应用在组合对象和个体对象上。

二、实现步骤

1、创建组件抽象类

也可以使用组件接口。

组件中有些方法可能不适合某种对象,此时我们可以抛异常或者提供特定实现。

/**
* 组件抽象类
*/
public abstract class Component { /**
* 子组件(可以是组合或叶节点)
*/
protected List<Component> childs = new ArrayList<Component>(); /**
* 添加子组件
*/
public void addChild(Component component) {
childs.add(component);
} /**
* 移除子组件
*/
public void removeChild(Component component) {
childs.remove(component);
} /**
* 获取所有子组件
*/
public List<Component> getChilds() {
return childs;
} public String getName() {
// 默认抛异常,由子类决定要不要覆盖
throw new UnsupportedOperationException();
}
}

2、创建组合及叶节点,并继承组件抽象类

(1)组合

组合可以包含其他组合,也可以包含叶节点。

/**
* 组合
*/
public class Composite extends Component { private String name; public Composite(String name) {
this.name = name;
} @Override
public String getName() {
return name;
}
}

(2)叶节点

叶节点无法添加、删除、获取子节点,因此需要对相应的方法进行特殊处理。

/**
* 叶节点
*/
public class Leaf extends Component { private String name; public Leaf(String name) {
this.name = name;
} @Override
public void addChild(Component component) {
// 叶节点不能添加子节点,可以抛异常或者空实现
throw new UnsupportedOperationException();
} @Override
public void removeChild(Component component) {
// 叶节点没有子节点可移除,可以抛异常或者空实现
throw new UnsupportedOperationException();
} @Override
public List<Component> getChilds() {
// 叶节点没有子节点,可以抛异常或者返回空集合
throw new UnsupportedOperationException();
} @Override
public String getName() {
return name;
}
}

3、统一使用组件的方法,操作组合及叶节点

由于组合及叶节点都实现了组件接口,因此可以使用组件的方法来操作组合及叶节点。

public class Test {

    public static void main(String[] args) {
// 组合
Component composite1 = new Composite("composite1");
Component composite2 = new Composite("composite2");
Component composite3 = new Composite("composite3");
// 叶节点
Component leaf1 = new Leaf("leaf1");
Component leaf2 = new Leaf("leaf2");
Component leaf3 = new Leaf("leaf3");
Component leaf4 = new Leaf("leaf4"); // 组合1包含组合2、3
composite1.addChild(composite2);
composite1.addChild(composite3);
// 组合2包含叶节点1、2
composite2.addChild(leaf1);
composite2.addChild(leaf2);
// 组合3包含叶节点3、4
composite3.addChild(leaf3);
composite3.addChild(leaf4); // 打印组件名称
System.out.println(composite1.getName());
for (Component child : composite1.getChilds()) {
System.out.println(" " + child.getName());
for (Component leaf : child.getChilds()) {
System.out.println(" " + leaf.getName());
}
}
}
}

三、举个栗子

1、背景

对象村餐厅和对象村煎饼屋合并了,它们合并后的新公司创建了一个 Java 版本的女招待。这个 Java 版本的女招待能够打印使用 ArrayList 存储的餐厅菜单和煎饼屋菜单。

现在它们打算加上一份餐后甜点的“子菜单”。也就是说,这个 Java 版本的女招待不仅要支持打印多个菜单,还要支持打印菜单中的菜单。

2、实现

由于菜单可能包含菜单和菜单项,因此我们可以创建一个树形结构,这个树形结构由菜单和菜单项组成。

通过将菜单和菜单项放在相同的结构中,我们既可以把整个结构视为一个“大菜单”,也可以把这个结构的任一部分视为一个“子菜单”,包括菜单项也可以视为一个“我本身就是菜单项因此没必要再包含菜单项的菜单”。这样,我们就可以以一致的方式来处理菜单和菜单项了。

(1)创建菜单组件抽象类

/**
* 菜单组件抽象类
*/
public abstract class MenuComponent { /**
* 子组件(可以是菜单或菜单项)
*/
protected List<MenuComponent> childs = new ArrayList<MenuComponent>(); /**
* 添加子组件
*/
public void addChild(MenuComponent component) {
childs.add(component);
} /**
* 移除子组件
*/
public void removeChild(MenuComponent component) {
childs.remove(component);
} /**
* 获取所有子组件
*/
public List<MenuComponent> getChilds() {
return childs;
} public String getName() {
throw new UnsupportedOperationException();
} public double getPrice() {
throw new UnsupportedOperationException();
} public void print() {
throw new UnsupportedOperationException();
}
}

(2)创建菜单,并继承菜单组件抽象类

/**
* 菜单(组合)
*/
public class Menu extends MenuComponent { private String name; public Menu(String name) {
this.name = name;
} @Override
public String getName() {
return name;
} @Override
public void print() {
System.out.println("\n" + getName());;
System.out.println("---------------------");;
}
}

(3)创建菜单项,并继承菜单组件抽象类

/**
* 菜单项(叶节点)
*/
public class MenuItem extends MenuComponent { private String name;
private double price; public MenuItem(String name, double price) {
this.name = name;
this.price = price;
} @Override
public void addChild(MenuComponent component) {
throw new UnsupportedOperationException();
} @Override
public void removeChild(MenuComponent component) {
throw new UnsupportedOperationException();
} @Override
public List<MenuComponent> getChilds() {
return childs;
} @Override
public String getName() {
return name;
} @Override
public double getPrice() {
return price;
} @Override
public void print() {
System.out.println(" " + getName() + ", " + getPrice());;
}
}

(4)创建女招待

/**
* 女招待
*/
public class Waitress { MenuComponent allMenus; public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
} public void printMenu() {
print(allMenus);
} private void print(MenuComponent menuComponent) {
menuComponent.print();
for (MenuComponent child : menuComponent.getChilds()) {
print(child);
}
}
}

(5)使用女招待打印菜单

public class Test {

    public static void main(String[] args) {
// 所有菜单
MenuComponent allMenus = new Menu("ALL MENUS");
// 子菜单
MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU");
MenuComponent dinerMenu = new Menu("DINER MENU");
MenuComponent dessertMenu = new Menu("DESSERT MENU"); // 添加煎饼屋菜单及菜单项
allMenus.addChild(pancakeHouseMenu);
pancakeHouseMenu.addChild(new MenuItem("Regular Pancake Breakfast", 2.99));
pancakeHouseMenu.addChild(new MenuItem("Blueberry Pancakes", 3.49));
pancakeHouseMenu.addChild(new MenuItem("Waffles", 3.59));
// 添加餐厅菜单及菜单项
allMenus.addChild(dinerMenu);
dinerMenu.addChild(new MenuItem("BLT", 2.99));
dinerMenu.addChild(new MenuItem("Soup of the day", 3.29));
dinerMenu.addChild(new MenuItem("Hotdog", 3.05));
// 添加甜点菜单及菜单项
dinerMenu.addChild(dessertMenu);
dessertMenu.addChild(new MenuItem("Apple Pie", 1.59));
dessertMenu.addChild(new MenuItem("Cheesecake", 1.99));
dessertMenu.addChild(new MenuItem("Sorbet", 1.89)); // 使用女招待打印菜单
Waitress waitress = new Waitress(allMenus);
waitress.printMenu();
}
}

《Head First 设计模式》:组合模式的更多相关文章

  1. 16. 星际争霸之php设计模式--组合模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  2. Java设计模式——组合模式

    JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...

  3. 【设计模式】Java设计模式 - 组合模式

    Java设计模式 - 组合模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自己 ...

  4. c#设计模式-组合模式

    在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象和复合对象 ...

  5. [Head First设计模式]生活中学设计模式——组合模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  6. JAVA 设计模式 组合模式

    用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模式. 结构

  7. javascript设计模式-组合模式

    组合模式所要解决的问题: 可以使用简单的对象组合成复杂的对象,而这个复杂对象有可以组合成更大的对象.可以把简单这些对象定义成类,然后定义一些容器类来存储这些简单对象. 客户端代码必须区别对象简单对象和 ...

  8. 设计模式组合模式(Composite)精华

    23种子GOF设计模式一般分为三类:创建模式.结构模型.行为模式. 创建模式抽象的实例,他们帮助如何创建一个系统独立.这是一个这些对象和陈述的组合. 创建使用继承类的类架构更改实例.的对象类型模型的建 ...

  9. 设计模式 -- 组合模式 (Composite Pattern)

    定义: 对象组合成部分整体结构,单个对象和组合对象具有一致性. 看了下大概结构就是集团总公司和子公司那种层级结构. 角色介绍: Component :抽象根节点:其实相当去总公司,抽象子类共有的方法: ...

  10. javascript设计模式——组合模式

    前面的话 在程序设计中,有一些和“事物是由相似的子事物构成”类似的思想.组合模式就是用小的子对象来构建更大的对象,而这些小的子对象本身也许是由更小的“孙对象”构成的.本文将详细介绍组合模式 宏命令 宏 ...

随机推荐

  1. C#LeetCode刷题之#374-猜数字大小(Guess Number Higher or Lower)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3993 访问. 我们正在玩一个猜数字游戏. 游戏规则如下: 我从 ...

  2. LeetCode 309 Best Time to Buy and Sell Stock with Cooldown 解决方案

    题目描述 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 .​ 设计一个算法计算出最大利润.在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 你不能同时参与多笔 ...

  3. windows安装cnpm步骤

    1.首先前往nodejs官网下载nodejs 2.安装nodejs 3.打开cmd,输入npm -v,检查npm是否安装成功.成功返回的话返回输出版本号 4.安装cnpm,输入npm install ...

  4. 使用Spring Boot开发者工具进行自动重启和页面自动刷新

    简介 大家可能都听说过开发Node.js应用时可以使用多种工具对开发者提供便利,如WebPack提供了开发者服务器来支持js应用动态更替,并在保存文件时自动刷新浏览器.Spring Boot也提供了相 ...

  5. hbase运行流程图

    hbase运行流程图

  6. maatwebsite lost precision when export long integer data

    Maatwebsite would lost precision when export long integer data, no matter string or int storaged in ...

  7. Netbox 开源 IPAM 管理工具搭建详细流程

    原文链接:Netbox 开源 IPAM 管理工具搭建详细流程 参考资料:https://netbox.readthedocs.io/en/stable/ PostgreSQL数据库安装 1.yum 下 ...

  8. CloudBase Framework丨第一个 Deno 部署工具是如何打造的?

    云端一体化部署工具 CloudBase Framework (简称 CBF)自开源发布以来迭代迅速,不仅支持 Vue.React 等前端框架,也支持 Nuxt 等 SSR 框架,基于 Node 开发的 ...

  9. IntelliJ IDEA下git版本回退,版本还原

    原文链接:https://blog.csdn.net/hehyyoulan/article/details/80005272

  10. C# 接口的Get、Post、WebService请求方法一览,值得收藏

    C# 接口的Get.Post.WebService请求方法一览,值得收藏 public static class HttpHelper { const string DEFAULT_USER_AGEN ...