介绍
组合模式组合多个对象形成树形结构以表示具有"部分-整体"关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,又可以称为"部分---整体"(Part-Whole)模式,它是一种对象结构型模式。
实现
myclass.h
cpp
//
// Created by yuwp on 2024/1/12.
//
#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H
#include <iostream>
#include <vector>
class Component { // 抽象基类
public:
virtual void operation();
virtual void add(Component *component);
virtual void remove(Component *component);
virtual Component *getChild(int i);
protected:
std::string m_name;
};
class LeafComponent : public Component { // 叶子节点具体类
public:
LeafComponent(const std::string &name);
void operation() override;
};
class CompositeComponent : public Component { // 容器节点具体类
public:
CompositeComponent(const std::string &name);
void operation() override;
void add(Component *component) override;
void remove(Component *component) override;
Component *getChild(int i) override;
private:
std::vector<Component *> m_components;
};
#endif //DESIGNPATTERNS_MYCLASS_H
myclass.cpp
cpp
//
// Created by yuwp on 2024/1/12.
//
#include "myclass.h"
void Component::operation() {
throw std::runtime_error("operation exception");
}
void Component::add(Component *component) {
throw std::runtime_error("add exception");
}
void Component::remove(Component *component) {
throw std::runtime_error("remove exception");
}
Component *Component::getChild(int i) {
throw std::runtime_error("getChild exception");
}
LeafComponent::LeafComponent(const std::string &name) {
m_name = name;
}
void LeafComponent::operation() {
std::cout << m_name << ": LeafComponent::operation()" << std::endl;
}
CompositeComponent::CompositeComponent(const std::string &name) {
m_name = name;
}
void CompositeComponent::operation() {
std::cout << m_name << ": CompositeComponent::operation()" << std::endl;
for (auto it = m_components.begin(); it != m_components.end(); ++it) {
(*it)->operation();
}
}
void CompositeComponent::add(Component *component) {
m_components.push_back(component);
}
void CompositeComponent::remove(Component *component) {
for (auto it = m_components.begin(); it != m_components.end(); ++it) {
if (*it == component) {
m_components.erase(it);
break;
}
}
}
Component *CompositeComponent::getChild(int i) {
if (i < m_components.size()) {
return m_components[i];
} else {
return nullptr;
}
}
main.cpp
cpp
#include <iostream>
#include <mutex>
#include "myclass.h"
int main() {
Component *leaf = new LeafComponent("leaf1");
Component *composite = new CompositeComponent("composite1");
Component *composite2 = new CompositeComponent("composite2");
composite->add(new LeafComponent("leaf2"));
composite->add(new LeafComponent("leaf3"));
composite2->add(new LeafComponent("leaf4"));
composite->add(composite2);
leaf->operation();
composite->operation();
try {
leaf->getChild(0);
} catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
return 0;
}
组合模式形式
透明组合模式
抽象构建Component中声明了所有用于管理成员对象的方法,这样能确保所有的构建类都有相同的接口。缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的。
组合安全模式
安全组合模式中,在抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法。这种做法是安全的,因为根本不向叶子对象提供这些管理成员对象的方法,对于叶子对象,客户端不可能调用到这些方法。缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。
总结
优点
-
组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次。它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
-
客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
-
在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合开闭原则。
-
组合模式为树形结构的面向对象实现提供了一种灵活的解决方案。通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
缺点
- 在增加新构件时很难对容器中的构件类型进行限制。
适用场景
-
在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致性地对待它们。
-
在一个使用面向对象语言开发的系统中需要处理一个树形结构。
-
在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,将来需要增加一些新的类型。
练习
myclass.h
cpp
//
// Created by yuwp on 2024/1/12.
//
#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H
#include <iostream>
#include <vector>
class Component {
public:
virtual void display();
virtual void add(Component *component);
virtual void remove(Component *component);
virtual Component *getChild(int i);
protected:
std::string m_name;
};
class Button : public Component {
public:
Button(const std::string &name);
void display() override;
};
class Text : public Component {
public:
Text(const std::string &name);
void display() override;
};
class Window : public Component {
public:
Window(const std::string &name);
void display() override;
void add(Component *component) override;
void remove(Component *component) override;
Component *getChild(int i) override;
private:
std::vector<Component *> m_childs;
};
class Panel : public Component {
public:
Panel(const std::string &name);
void display() override;
void add(Component *component) override;
void remove(Component *component) override;
Component *getChild(int i) override;
private:
std::vector<Component *> m_childs;
};
#endif //DESIGNPATTERNS_MYCLASS_H
myclass.cpp
cpp
//
// Created by yuwp on 2024/1/12.
//
#include "myclass.h"
void Component::display() {
throw std::runtime_error("operation exception");
}
void Component::add(Component *component) {
throw std::runtime_error("add exception");
}
void Component::remove(Component *component) {
throw std::runtime_error("remove exception");
}
Component *Component::getChild(int i) {
throw std::runtime_error("getChild exception");
}
Button::Button(const std::string &name) {
m_name = name;
}
void Button::display() {
std::cout << "Button: " << m_name << std::endl;
}
Text::Text(const std::string &name) {
m_name = name;
}
void Text::display() {
std::cout << "Text: " << m_name << std::endl;
}
Window::Window(const std::string &name) {
m_name = name;
}
void Window::display() {
std::cout << "Window: " << m_name << "包含: " << std::endl;
for (auto it = m_childs.begin(); it != m_childs.end(); ++it) {
(*it)->display();
}
}
void Window::add(Component *component) {
m_childs.push_back(component);
}
void Window::remove(Component *component) {
for (auto it = m_childs.begin(); it != m_childs.end(); ++it) {
if (*it == component) {
m_childs.erase(it);
break;
}
}
}
Component *Window::getChild(int i) {
if (i < m_childs.size()) {
return m_childs[i];
} else {
return nullptr;
}
}
Panel::Panel(const std::string &name) {
m_name = name;
}
void Panel::display() {
std::cout << "Panel: " << m_name << "包含: " << std::endl;
for (auto it = m_childs.begin(); it != m_childs.end(); ++it) {
(*it)->display();
}
}
void Panel::add(Component *component) {
m_childs.push_back(component);
}
void Panel::remove(Component *component) {
for (auto it = m_childs.begin(); it != m_childs.end(); ++it) {
if (*it == component) {
m_childs.erase(it);
break;
}
}
}
Component *Panel::getChild(int i) {
if (i < m_childs.size()) {
return m_childs[i];
} else {
return nullptr;
}
}