C++ 设计模式 —— 组合模式
0. 引用连接
本文主要的思路和代码,来自于对以下连接的学习和实现:
1. 引言
1.1 什么是组合模式?
- 组合模式的定义
- 组合模式的作用
组合模式是一种行为型设计模式,它将对象组合成树形结构以表示部分 - 整体的层次结构。这种模式使得用户对单个对象和组合对象的使用具有一致性。组合模式主要应用于需要表示复杂对象结构或者需要将对象组合成树形结构的场景。
组合模式的定义和作用:
定义:组合模式通过一种巧妙的设计方案,可以一致性地处理整个树形结构或者树形结构的一部分,也可以一致性地处理树形结构中的叶子节点(不包含子节点的节点)和容器节点。
作用:组合模式主要解决了在对象组合树中,如何实现一致性的操作和处理的问题。它使得用户可以方便地对对象树进行操作,而不需要考虑对象的具体类型。组合模式还提供了一种灵活的方式来表示对象结构,可以方便地添加和删除对象。
1.2 组合模式与其他设计模式的关系
组合模式是行为型设计模式的一种,它将对象组合成树形结构以表示部分 - 整体的层次结构。在实际应用中,组合模式与其他设计模式有着紧密的联系,常常共同出现在同一个解决方案中。以下是组合模式与其他设计模式的一些关系:
- 装饰者模式 : 装饰者模式与组合模式在结构上有相似之处,但它们的目的不同。装饰者模式通过动态地给一个对象添加职责,使其具有更多的功能,而组合模式关注的是将对象组合成树形结构以表示部分 - 整体的层次结构。两者都依赖于递归组合来组织大量对象。在某些情况下,装饰者模式和组合模式可以结合使用,以实现更加灵活的对象结构。
- 责任链模式 : 责任链模式用于处理具有多个处理步骤的问题,它将请求沿着处理器链进行传递。组合模式则关注将对象组合成树形结构以表示部分 - 整体的层次结构。虽然这两个模式的用途不同,但它们可以结合使用。例如,可以使用组合模式构建一个复杂的对象结构,然后使用责任链模式处理该结构中的请求。
- 迭代器模式 : 迭代器模式允许在不暴露底层数据结构的情况下遍历和访问数据。在组合模式中,可以使用迭代器来遍历复合对象的子元素。迭代器模式抽象了遍历组件的过程,确保每个元素都被访问到,同时不暴露底层数据结构的细节。
- 访问者模式 : 访问者模式允许在不改变对象结构的前提下定义新的操作。在组合模式中,可以将访问者模式应用于复合结构,以便在不更改现有类层次结构的情况下对元素执行操作。访问者模式使您可以在现有代码中添加新行为,而无需修改现有代码。
总之,在实际软件开发中,设计模式往往不会孤立存在。组合模式与其他设计模式之间的紧密联系使得开发人员可以根据具体需求和场景,将这些模式结合使用,以实现更加灵活、高效和可扩展的软件系统。
1.3 组合模式适用的场景
组合模式是一种行为型设计模式,适用于多种场景,如树形对象结构、统一对象处理、动态责任、可变责任和 UI 工具包等。在实际应用中,组合模式与其他设计模式密切相关,共同解决各种问题。以下是组合模式与其他设计模式的一些关系:
- 树形对象结构 : 组合模式适用于实现树形对象结构,如组织结构、文件系统、UI 元素等。这种结构可以方便地表示部分 - 整体的层次关系。在组合模式中,对象通过递归组合形成树状结构,使得客户代码可以统一处理简单元素和复杂元素。
- 统一对象处理 : 组合模式允许客户代码以统一的方式处理简单元素和复杂元素。这种统一处理方式可以简化客户端代码,提高代码的可维护性和可扩展性。通过组合模式,客户端可以忽略对象的复杂内部结构,而仅关注对象的整体行为。
- 动态责任 : 组合模式允许在运行时动态地为单个对象添加责任,而无需修改现有代码。这种动态责任分配可以提高系统的灵活性,使其能够适应不断变化的需求。在组合模式中,可以通过将新的责任分配给现有的对象来实现动态责任。
- 可变责任 : 组合模式适用于责任可能随时间变化的场景。在组合模式中,对象可以具有不同的责任,这使得系统能够适应不断变化的需求。通过组合模式,可以轻松地为对象添加新的责任,而无需修改现有代码。
- UI 工具包 : 组合模式在 UI 工具包中具有广泛的应用。在这种场景下,顶级 UI 元素由许多较小的独立底层 UI 元素组成,这些元素都响应相同的事件和操作。通过使用组合模式,可以轻松地管理 UI 元素的层次结构,并确保它们以一致的方式响应事件。
- 计费系统 : 在计费系统中,组合模式可以应用于处理复杂的活动记录。在这种场景下,重要的是能够生成正确的计费,而不关心活动的具体细节。通过使用组合模式,可以将复杂的活动记录组织成一个树形结构,以便生成正确的计费。
组合模式与其他设计模式密切相关,它们共同解决各种实际问题。在实际应用中,可以根据具体需求和场景,将组合模式与其他设计模式结合使用,以实现更加灵活、高效和可扩展的软件系统。
2. 组合模式的实现
2.1 组合模式概念伪代码
2.1.1 图形概念组合伪代码
CompoundGraphic
类是一个容器,可以包含任意数量的子形状,包括其他复合形状。复合形状具有与简单形状相同的方法。但是,与简单形状不同,复合形状会将请求递归地传递给所有子项,并将结果“求和”。
客户端代码通过与所有形状共享的单一接口与所有形状进行交互。因此,客户端不知道自己正在处理简单形状还是复合形状。客户端可以在不与组成该结构的具体类耦合的情况下处理非常复杂的对象结构。
代码结构分析:
- 定义复合图形类,包含初始化方法和递归调用方法。
- 定义简单图形类,包含初始化方法和基本操作方法。
- 定义客户端代码,通过与所有形状共享的单一接口与所有形状进行交互。
- 在客户端代码中,根据需要创建复合图形对象和简单图形对象。
- 使用复合图形对象和简单图形对象执行各种操作,如添加、删除、修改等。
- 验证客户端代码是否正确处理了复合图形对象和简单图形对象的操作。
// The component interface declares common operations for both
// simple and complex objects of a composition.
interface Graphic is
method move(x, y)
method draw()
// The leaf class represents end objects of a composition. A
// leaf object can't have any sub-objects. Usually, it's leaf
// objects that do the actual work, while composite objects only
// delegate to their sub-components.
class Dot implements Graphic is
field x, y
constructor Dot(x, y) {
... }
method move(x, y) is
this.x += x, this.y += y
method draw() is
// Draw a dot at X and Y.
// All component classes can extend other components.
class Circle extends Dot is
field radius
constructor Circle(x, y, radius) {
... }
method draw() is
// Draw a circle at X and Y with radius R.
// The composite class represents complex components that may
// have children. Composite objects usually delegate the actual
// work to their children and then "sum up" the result.
class CompoundGraphic implements Graphic is
field children: array of Graphic
// A composite object can add or remove other components
// (both simple or complex) to or from its child list.
method add(child: Graphic) is
// Add a child to the array of children.
method remove(child: Graphic) is
// Remove a child from the array of children.
method move(x, y) is
foreach (child in children) do
child.move(x, y)
// A composite executes its primary logic in a particular
// way. It traverses recursively through all its children,
// collecting and summing up their results. Since the
// composite's children pass these calls to their own
// children and so forth, the whole object tree is traversed
// as a result.
method draw() is
// 1. For each child component:
// - Draw the component.
// - Update the bounding rectangle.
// 2. Draw a dashed rectangle using the bounding
// coordinates.
// The client code works with all the components via their base
// interface. This way the client code can support simple leaf
// components as well as complex composites.
class ImageEditor is
field all: CompoundGraphic
method load() is
all = new CompoundGraphic()
all.add(new Dot(1, 2))
all.add(new Circle(5, 3, 10))
// ...
// Combine selected components into one complex composite
// component.
method groupSelected(components: array of Graphic) is
group = new CompoundGraphic()
foreach (component in components) do
group.add(component)
all.remove(component)
all.add(group)
// All components will be drawn.
all.draw()
2.1.2 叶子组合伪代码
组合模式是使用抽象基类或接口Component
实现的,它声明了公共操作。两个具体类Leaf
和Composite
继承自Component
。以下是这些概念的伪代码描述:
class Component {
public:
virtual void Add(Component a) {
}
virtual void Remove() {
}
virtual void Delete(Component a) {
}
}