《设计模式的艺术》笔记 - 组合模式

介绍

组合模式组合多个对象形成树形结构以表示具有"部分-整体"关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,又可以称为"部分---整体"(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类中声明并实现这些方法。这种做法是安全的,因为根本不向叶子对象提供这些管理成员对象的方法,对于叶子对象,客户端不可能调用到这些方法。缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。

总结

优点

  1. 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次。它让客户端忽略了层次的差异,方便对整个层次结构进行控制。

  2. 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。

  3. 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合开闭原则。

  4. 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案。通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

缺点

  1. 在增加新构件时很难对容器中的构件类型进行限制。

适用场景

  1. 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致性地对待它们。

  2. 在一个使用面向对象语言开发的系统中需要处理一个树形结构。

  3. 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,将来需要增加一些新的类型。

练习

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;
    }
}
相关推荐
诸葛悠闲43 分钟前
设计模式——桥接模式
设计模式·桥接模式
oneouto2 小时前
selenium学习笔记(二)
笔记·学习·selenium
sealaugh322 小时前
aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发
笔记·学习·aws
LuH11244 小时前
【论文阅读笔记】Scalable, Detailed and Mask-Free Universal Photometric Stereo
论文阅读·笔记
捕鲸叉5 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~5 小时前
框架专题:设计模式
设计模式·框架
先睡5 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
m0_748256786 小时前
WebGIS实战开源项目:智慧机场三维可视化(学习笔记)
笔记·学习·开源
红色的山茶花6 小时前
YOLOv9-0.1部分代码阅读笔记-loss.py
笔记
胡西风_foxww8 小时前
【es6复习笔记】Promise对象详解(12)
javascript·笔记·es6·promise·异步·回调·地狱