c/c++设计模式--组合模式

 

 

namespace _nmsp1
{    
    //文件相关类
    class File
    {
    public:
        //构造函数
        File(string name) :m_sname(name) {}

        //显示文件名
        void ShowName(string lvlstr) //lvlstr:为了显示层次关系的缩进字符串内容
        {
            cout << lvlstr << "-" << m_sname << endl; //显示“-”代表是一个文件,属末端节点(不会再有子节点)
        }

    private:
        string m_sname; //文件名
    };

    //目录相关类
    class Dir
    {
    public:
        Dir(string name) :m_sname(name) {}

    public:
        //目录中可以增加其他文件
        void AddFile(File* pfile)
        {
            m_childFile.push_back(pfile);
        }
        //目录中可以增加其他目录
        void AddDir(Dir* pdir)
        {
            m_childDir.push_back(pdir);
        }

        //显示目录名,同时也要负责其下面的文件和目录名的显示工作
        void ShowName(string lvlstr)//lvlstr:为了显示层次关系的缩进字符串内容
        {
            //(1)输出本目录名
            cout << :q << "+" << m_sname << endl; //显示“+”代表是一个目录,其中会包含其他内容

            //(2)输出所包含的文件名
            lvlstr += "    "; //本目录中的文件和目录的显示,要缩进一些来显示
            for (auto iter = m_childFile.begin(); iter != m_childFile.end(); ++iter)
            {
                (*iter)->ShowName(lvlstr); //显示文件名
            }

            //(3)输出所包含的目录名
            for (auto iter = m_childDir.begin(); iter != m_childDir.end(); ++iter)
            {
                (*iter)->ShowName(lvlstr); //显示目录名,这里涉及了递归调用 
            }
        }

    private:
        string m_sname; //目录名
        list<File*> m_childFile; //目录中包含的文件列表
        list<Dir*> m_childDir; //目录中包含的子目录列表
    };

}

int main()
{

/*
    //(1)创建各种目录,文件对象
    _nmsp1::Dir *pdir1 = new _nmsp1::Dir("root");
    //----
    _nmsp1::File* pfile1 = new  _nmsp1::File("common.mk");
    _nmsp1::File* pfile2 = new  _nmsp1::File("config.mk");
    _nmsp1::File* pfile3 = new  _nmsp1::File("makefile");
    //-----
    _nmsp1::Dir* pdir2 = new _nmsp1::Dir("app");
    _nmsp1::File* pfile4 = new  _nmsp1::File("nginx.c");
    _nmsp1::File* pfile5 = new  _nmsp1::File("ngx_conf.c");
    //-----
    _nmsp1::Dir* pdir3 = new _nmsp1::Dir("signal");
    _nmsp1::File* pfile6 = new  _nmsp1::File("ngx_signal.c");
    //-----
    _nmsp1::Dir* pdir4 = new _nmsp1::Dir("_include");
    _nmsp1::File* pfile7 = new  _nmsp1::File("ngx_func.h");
    _nmsp1::File* pfile8 = new  _nmsp1::File("ngx_signal.h");

    //(2)构造树形目录结构
    pdir1->AddFile(pfile1);
    pdir1->AddFile(pfile2);
    pdir1->AddFile(pfile3);
    //----
    pdir1->AddDir(pdir2);
        pdir2->AddFile(pfile4);
        pdir2->AddFile(pfile5);
    //----
    pdir1->AddDir(pdir3);
        pdir3->AddFile(pfile6);
    //----
    pdir1->AddDir(pdir4);
        pdir4->AddFile(pfile7);
        pdir4->AddFile(pfile8);


    //(3)输出整个目录结构,只要通过根目录的ShowName方法即可,每个子目录都有自己的ShowName方法负责自己旗下的文件和目录显示。
    pdir1->ShowName(""); //缩进字符刚开始可以为空
    //pdir2->ShowName("");

    //(4)释放资源
    delete pfile8;
    delete pfile7;
    delete pdir4;
    //----
    delete pfile6;
    delete pdir3;
    //----
    delete pfile5;
    delete pfile4;
    delete pdir2;
    //----
    delete pfile3;
    delete pfile2;
    delete pfile1;
    delete pdir1;
    */

}

使用组合模式

namespace _nmsp2
{
    //抽象父类FileSystem(抽象接口)
    class FileSystem
    {
    public:
        virtual void ShowName(int level) = 0; //显示名字,参数level用于表示显示的层次,用于显示对齐
        virtual int Add(FileSystem* pfilesys) = 0; //向当前目录中增加文件或者子目录
        virtual int Remove(FileSystem* pfilesys) = 0; //从当前目录中移除文件或者子目录
        
        virtual ~FileSystem() {}   //做父类时析构函数应该为虚函数
    };

    //文件相关类
    class File :public FileSystem
    {
    public:
        //构造函数
        File(string name) :m_sname(name) {}
        //显示名
        virtual void ShowName(int level)
        {
            for (int i = 0; i < level; ++i)
            {
                cout << "    "; //显示若干个空格用于对齐
            }
            cout << "-" << m_sname << endl;
        }
        virtual int Add(FileSystem* pfilesys)
        {
            return -1;
        }
        virtual int Remove(FileSystem* pfilesys)
        {
            return -1;
        }

    private:
        string m_sname; //文件名
    };

    //目录相关类
    class Dir :public FileSystem
    {
    public:
        //构造函数
        Dir(string name) :m_sname(name) {}

        //显示名字
        virtual void ShowName(int level)
        {
            //(1)显示若干个空格用于对齐
            for (int i = 0; i < level; ++i) { cout << "    "; }
            //(2)输出本目录名
            cout << "+" << m_sname << endl;
            //(3)显示的层级向下走一级
            level++;
            //(4)输出所包含的子内容(可能是文件,也可能是目录)
            //遍历目录中的文件和子目录
            for (auto iter = m_child.begin(); iter != m_child.end(); ++iter)
            {
                (*iter)->ShowName(level);
            }
        }

        virtual int Add(FileSystem* pfilesys)
        {
            m_child.push_back(pfilesys);
            return 0;
        }

        virtual int Remove(FileSystem* pfilesys)
        {
            m_child.remove(pfilesys);
            return 0;
        }

    private:
        string m_sname; //文件名
        list<FileSystem*> m_child; //目录中包含的文件或者其他目录列表
    };
}

int main()
{
  //(1)创建各种目录,文件对象
    _nmsp2::FileSystem* pdir1 = new _nmsp2::Dir("root");
    //----
    _nmsp2::FileSystem* pfile1 = new  _nmsp2::File("common.mk");
    _nmsp2::FileSystem* pfile2 = new  _nmsp2::File("config.mk");
    _nmsp2::FileSystem* pfile3 = new  _nmsp2::File("makefile");
    //-----
    _nmsp2::FileSystem* pdir2 = new _nmsp2::Dir("app");
    _nmsp2::FileSystem* pfile4 = new  _nmsp2::File("nginx.c");
    _nmsp2::FileSystem* pfile5 = new  _nmsp2::File("ngx_conf.c");
    //-----
    _nmsp2::FileSystem* pdir3 = new _nmsp2::Dir("signal");
    _nmsp2::FileSystem* pfile6 = new  _nmsp2::File("ngx_signal.c");
    //-----
    _nmsp2::FileSystem* pdir4 = new _nmsp2::Dir("_include");
    _nmsp2::FileSystem* pfile7 = new  _nmsp2::File("ngx_func.h");
    _nmsp2::FileSystem* pfile8 = new  _nmsp2::File("ngx_signal.h");

    //(2)构造树形目录结构
    pdir1->Add(pfile1);
    pdir1->Add(pfile2);
    pdir1->Add(pfile3);
    //----
    pdir1->Add(pdir2);
    pdir2->Add(pfile4);
    pdir2->Add(pfile5);
    //----
    pdir1->Add(pdir3);
    pdir3->Add(pfile6);
    //----
    pdir1->Add(pdir4);
    pdir4->Add(pfile7);
    pdir4->Add(pfile8);


    //(3)输出整个目录结构,只要通过根目录的ShowName方法即可,每个子目录都有自己的ShowName方法负责自己旗下的文件和目录显示。
    pdir1->ShowName(0); //缩进字符刚开始可以为空
    //pdir2->ShowName("");

    //(4)释放资源
    delete pfile8;
    delete pfile7;
    delete pdir4;
    //----
    delete pfile6;
    delete pdir3;
    //----
    delete pfile5;
    delete pfile4;
    delete pdir2;
    //----
    delete pfile3;
    delete pfile2;
    delete pfile1;
    delete pdir1;

    return 0;

}

 

 

namespace _nmsp3
{
    class Dir;
    //抽象父类FileSystem(抽象接口)
    class FileSystem
    {
    public:
        virtual void ShowName(int level) = 0; //显示名字,参数level用于表示显示的层次,用于显示对齐

        virtual int countNumOfFiles() = 0; //统计目录下包含的文件个数
        
        virtual Dir* ifCompositeObj() { return nullptr; } //判断是否是一个树枝(组合对象)
        virtual ~FileSystem() {} //做父类时析构函数应该为虚函数
    };

    //文件相关类
    class File :public FileSystem
    {
    public:
        //构造函数
        File(string name) :m_sname(name) {}
        //显示名
        virtual void ShowName(int level)
        {
            for (int i = 0; i < level; ++i)
            {
                cout << "    "; //显示若干个空格用于对齐
            }
            cout << "-" << m_sname << endl;
        }        

        virtual int countNumOfFiles()//统计目录下包含的文件个数
        {
            return 1; //文件节点,做数量统计时按1计算
        }

    private:
        string m_sname; //文件名
    };

    //目录相关类
    class Dir :public FileSystem
    {
    public:
        //构造函数
        Dir(string name) :m_sname(name) {}

        //显示名字
        virtual void ShowName(int level)
        {
            //(1)显示若干个空格用于对齐
            for (int i = 0; i < level; ++i) { cout << "    "; }
            //(2)输出本目录名
            cout << "+" << m_sname << endl;
            //(3)显示的层级向下走一级
            level++;
            //(4)输出所包含的子内容(可能是文件,也可能是目录)
            //遍历目录中的文件和子目录
            for (auto iter = m_child.begin(); iter != m_child.end(); ++iter)
            {
                (*iter)->ShowName(level);
            }
        }

        int Add(FileSystem* pfilesys)
        {
            m_child.push_back(pfilesys);
            return 0;
        }

        int Remove(FileSystem* pfilesys)
        {
            m_child.remove(pfilesys);
            return 0;
        }

        virtual Dir* ifCompositeObj() { return this; }

        virtual int countNumOfFiles()//统计目录下包含的文件个数
        {
            int iNumOfFiles = 0;
            for (auto iter = m_child.begin(); iter != m_child.end(); ++iter)
            {
                iNumOfFiles += (*iter)->countNumOfFiles(); //递归调用
            }
            return iNumOfFiles;
        }

    private:
        string m_sname; //文件名
        list<FileSystem*> m_child; //目录中包含的文件或者其他目录列表
    };
}

int main()
{

/(1)创建各种目录,文件对象
    _nmsp3::Dir* pdir1 = new _nmsp3::Dir("root");
    //----
    _nmsp3::FileSystem* pfile1 = new  _nmsp3::File("common.mk");
    _nmsp3::FileSystem* pfile2 = new  _nmsp3::File("config.mk");
    _nmsp3::FileSystem* pfile3 = new  _nmsp3::File("makefile");
    //-----
    _nmsp3::Dir* pdir2 = new _nmsp3::Dir("app");
    _nmsp3::FileSystem* pfile4 = new  _nmsp3::File("nginx.c");
    _nmsp3::FileSystem* pfile5 = new  _nmsp3::File("ngx_conf.c");
    //-----
    _nmsp3::Dir* pdir3 = new _nmsp3::Dir("signal");
    _nmsp3::FileSystem* pfile6 = new  _nmsp3::File("ngx_signal.c");
    //-----
    _nmsp3::Dir* pdir4 = new _nmsp3::Dir("_include");
    _nmsp3::FileSystem* pfile7 = new  _nmsp3::File("ngx_func.h");
    _nmsp3::FileSystem* pfile8 = new  _nmsp3::File("ngx_signal.h");

    //(2)构造树形目录结构
    if (pdir2->ifCompositeObj() != nullptr)
    {
        //是个组合对象,可以向其中增加单个对象和其他组合对象。
        //.....
    }
    if (dynamic_cast<_nmsp3::Dir*>(pdir2) != nullptr)
    {
        //是个组合对象,可以向其中增加单个对象和其他组合对象。
        //.....

    }
    pdir1->Add(pfile1);
    //pfile1->Add(pfile2);不可以向单个对象中加入其他对象
    pdir1->Add(pfile2);
    pdir1->Add(pfile3);
    //----
    pdir1->Add(pdir2);
    pdir2->Add(pfile4);
    pdir2->Add(pfile5);
    //----
    pdir1->Add(pdir3);
    pdir3->Add(pfile6);
    //----
    pdir1->Add(pdir4);
    pdir4->Add(pfile7);
    pdir4->Add(pfile8);


    //(3)输出整个目录结构,只要通过根目录的ShowName方法即可,每个子目录都有自己的ShowName方法负责自己旗下的文件和目录显示。
    pdir1->ShowName(0); //缩进字符刚开始可以为空
    //pdir2->ShowName("");

    cout << "文件的数量为:" << pdir1->countNumOfFiles() << endl;

    //(4)释放资源
    delete pfile8;
    delete pfile7;
    delete pdir4;
    //----
    delete pfile6;
    delete pdir3;
    //----
    delete pfile5;
    delete pfile4;
    delete pdir2;
    //----
    delete pfile3;
    delete pfile2;
    delete pfile1;
    delete pdir1;

    return 0;

}

 

    组合(Composite)模式:结构型模式,用来处理树形结构的数据
    //(1)一个基本的目录内容遍历范例:用来表达和处理树形结构数据
    //(2)使用组合模式改造目录内容遍历范例
    //(3)引入组合(Composite)模式:TreeCtrl,TreeView
    //定义:将一组对象(文件和目录)组织成树形结构以表示“部分-整体”的层次结构(目录中包含文件和子目录)。使得用户对单个对象(文件)
       //和组合对象(目录)的操作/使用/处理(递归遍历并执行ShowName的逻辑)具有一致性。
    //发挥作用的前提:具体数据必须能以树形结构的方式表示,树中包含了单个和组合对象。该模式专注于树形结构中单个对象和组合对象的递归遍历
        //并且能把相同的操作(FileSystem定义的接口)应用在单个以及组合对象上并且可以忽略单个对象和组合对象之间的差别。
    //3种角色:
    //a)Component(抽象组件):FileSystem类。
    //b)Leaf(叶子组件):File类。 KillVirus  --ExeFile, ImgFile类
    //c)Composite(树枝组件):Dir类。
    //Frame,Panel---文本,线,矩形,按钮,菜单,滚动

    //优点:a)简化代码书写。b)符合开闭原则。c)处理复杂的树形结构。

    //(4)透明组合模式与安全组合模式
    //透明组合模式:在抽象组件(FileSystem类)中声明了所有用于管理和访问子节点的成员函数的实现手段。
    //安全组合模式:抽象组件中用于管理和访问子节点的成员函数被转义到了树枝组件中。

    //(5)其他使用组合模式的场景探讨
    //a)公司组织结构
    //b)杀毒:exe .com ---pdir->KillVirus();
    //c)利用图元进行图形的绘制工作

 

使用C++实现组合模式时,可以考虑一个简单的文件系统结构作为例子。在这个例子中,我们可以定义一个抽象基类 Component 代表文件系统中的文件和文件夹,然后派生出 Leaf 类表示文件,以及 Composite 类表示文件夹。

以下是一个简化的示例代码:

#include <iostream>
#include <string>
#include <vector>

// 抽象基类 Component
class Component {
public:
    virtual void display() = 0;
    virtual ~Component() {}
};

// Leaf 类表示文件
class File : public Component {
private:
    std::string name;

public:
    File(const std::string& name) : name(name) {}
    void display() override {
        std::cout << "File: " << name << std::endl;
    }
};

// Composite 类表示文件夹
class Folder : public Component {
private:
    std::string name;
    std::vector<Component*> children;

public:
    Folder(const std::string& name) : name(name) {}
    void add(Component* component) {
        children.push_back(component);
    }
    void display() override {
        std::cout << "Folder: " << name << std::endl;
        for (Component* child : children) {
            child->display();
        }
    }
};

int main() {
    // 创建文件和文件夹
    File file1("file1.txt");
    File file2("file2.txt");
    Folder folder1("Folder1");
    folder1.add(&file1);
    folder1.add(&file2);

    File file3("file3.txt");
    Folder folder2("Folder2");
    folder2.add(&file3);
    folder2.add(&folder1);

    // 显示文件系统结构
    folder2.display();

    return 0;
}

在上面的示例中,我们定义了抽象基类 Component,并创建了 File 和 Folder 两种具体的子类。通过组合的方式,我们可以轻松地构建出文件和文件夹的层次结构,并且调用 display 方法可以统一地展示文件系统中的所有内容,即使它们是文件还是文件夹。这就展示了组合模式的灵活性和一致性。

 
 

想象你有一个树形结构,树的每个节点都可以有子节点,也可以没有。这个树可以表示不同层级的组织结构,比如公司的部门结构。

在组合模式中,树的节点被抽象为统一的对象,无论是叶子节点(没有子节点的节点)还是组合节点(有子节点的节点)。这样,你就可以统一地处理树的节点,而不需要知道它是叶子节点还是组合节点。

举个例子,假设你有一个组织结构的树,根节点代表公司,它有多个子节点代表不同的部门,每个部门又可以有自己的子部门,最后的叶子节点代表员工。现在,如果你想要计算公司的总人数,你可以使用组合模式。

以下是一个简单的C++例子:

#include <iostream>
#include <string>
#include <vector>

// 抽象的组织结构节点
class OrganizationComponent {
public:
    virtual int getEmployeeCount() = 0;
    virtual ~OrganizationComponent() {}
};

// 叶子节点:员工
class Employee : public OrganizationComponent {
private:
    std::string name;

public:
    Employee(const std::string& name) : name(name) {}
    int getEmployeeCount() override {
        return 1;
    }
};

// 组合节点:部门
class Department : public OrganizationComponent {
private:
    std::string name;
    std::vector<OrganizationComponent*> children;

public:
    Department(const std::string& name) : name(name) {}
    void add(OrganizationComponent* component) {
        children.push_back(component);
    }
    int getEmployeeCount() override {
        int count = 0;
        for (OrganizationComponent* component : children) {
            count += component->getEmployeeCount();
        }
        return count;
    }
};

int main() {
    // 创建组织结构
    Department company("Company");

    Department hrDepartment("HR Department");
    hrDepartment.add(new Employee("Alice"));
    hrDepartment.add(new Employee("Bob"));

    Department engineeringDepartment("Engineering Department");
    engineeringDepartment.add(new Employee("Charlie"));
    engineeringDepartment.add(new Employee("David"));
    engineeringDepartment.add(new Employee("Eve"));

    company.add(&hrDepartment);
    company.add(&engineeringDepartment);

    // 计算公司总人数
    std::cout << "Total employees: " << company.getEmployeeCount() << std::endl;

    return 0;
}

在这个例子中,Employee 类表示叶子节点(员工),而 Department 类表示组合节点(部门)。无论是叶子节点还是组合节点,它们都实现了统一的接口 OrganizationComponent。这样,我们可以将它们放入同一个树形结构中,而不需要在处理它们的时候区分它们的类型。

"安全组合模式"是组合模式的一种变体,它通过在组件接口中去除对不适用操作的支持,从而保证了安全性。在安全组合模式中,将不适用的方法移除或者抛出异常,这样可以确保叶子对象和组合对象具有一致的接口,并且不会导致不适用的操作被调用。

在标准的组合模式中,通常会在组件接口中包含所有可能的操作,但是对于叶子对象来说,一些操作是不适用的。为了解决这个问题,安全组合模式通过在组件接口中去除这些不适用的操作,或者在叶子对象中抛出异常,来确保这些不适用的操作不会被调用。

安全组合模式的优点在于它提供了一种更加严格和安全的方式来使用组合模式,使得客户端代码更加清晰和可靠。然而,这也意味着在处理组件时,客户端需要更多的注意力,以避免调用不适用的操作。

总之,安全组合模式是组合模式的一种变体,它通过限制组件接口中的操作来确保安全性,尽管这可能会增加客户端的复杂性,但是能够提供更加清晰和可靠的

 
来举一个简单的例子来说明安全组合模式。假设我们要构建一个表示公司组织结构的类,其中包括部门和雇员。我们使用安全组合模式来实现这个结构。

首先,我们定义一个组件接口 CompanyUnit,其中包含了所有可能的操作,比如获取名称、添加子部门或员工、移除子部门或员工等。然后我们创建两个具体的类,分别是 DepartmentEmployee,它们都实现了 CompanyUnit 接口。

#include <iostream>
#include <vector>
#include <stdexcept>

class CompanyUnit {
public:
    virtual std::string getName() const = 0;
    virtual void add(CompanyUnit* unit) {
        throw std::logic_error("Unsupported operation");
    }
    virtual void remove(CompanyUnit* unit) {
        throw std::logic_error("Unsupported operation");
    }
    virtual std::vector<CompanyUnit*> getSubordinates() const {
        throw std::logic_error("Unsupported operation");
    }
    virtual ~CompanyUnit() {}
};

然后,我们创建两个具体的类 DepartmentEmployee,它们都继承自 CompanyUnit

class Department : public CompanyUnit {
private:
    std::string name;
    std::vector<CompanyUnit*> subordinates;

public:
    Department(const std::string& name) : name(name) {}

    std::string getName() const override {
        return name;
    }

    void add(CompanyUnit* unit) override {
        subordinates.push_back(unit);
    }

    void remove(CompanyUnit* unit) override {
        // 实现根据需要移除员工或子部门的逻辑
    }

    std::vector<CompanyUnit*> getSubordinates() const override {
        return subordinates;
    }

    ~Department() {
        for (auto& unit : subordinates) {
            delete unit;
        }
    }
};

class Employee : public CompanyUnit {
private:
    std::string name;

public:
    Employee(const std::string& name) : name(name) {}

    std::string getName() const override {
        return name;
    }
};

在这个例子中,我们使用了安全组合模式来确保叶子对象 Employee 不支持添加子部门或雇员的操作,当客户端尝试调用这些不适用的操作时,会抛出 UnsupportedOperationException 异常,以确保安全性。

通过这个例子,你可以看到安全组合模式如何确保了操作的安全性,并提供了一种清晰的方式来处理组合结构

posted @ 2024-05-31 18:31  白伟碧一些小心得  阅读(17)  评论(0编辑  收藏  举报