zoukankan      html  css  js  c++  java
  • 设计模式之实现组合模式

    /******************************************************

    baseobject.h

    ******************************************************/

    #pragma once
    #include <iostream>
    #include <string>
    #include <vector>
    #include "CIterator.h"
    #define MAX_ARRAY_SIZE 100
    using namespace std;

    //前置定义
    class CIteratorInterface;
    class CElectricIterator;

    ///////////////////////////////////////////////
    //******************
    //组合模式的基类,CCompenent。只留接口。把成员留给子类
    //组合有两种,分别是组合本身(递归)及其叶子
    //////////////////////////////////////////////
    class CComponent
    {
    protected:
     //设假设名字是唯一的。这里就不去做ID的唯一了。
     string m_Name;

     bool   m_bIsComponentOrLeaf;
    public:

     //组合类的三个基本方法,只能由非叶子结点来操作
     //在这里例子中就是Menu
     virtual void addComponent(CComponent* menuComponent) {
      //这里可以直接抛出一个说明这个操作不被允许的异常
      return;  
     }

     virtual void removeComponent(CComponent* rmComponent) {
      return;
     }

     virtual CComponent* getChild(unsigned long  i) {
      return NULL;
     }

     //其它菜单项与菜单共有的方法
     virtual string getName()
     {
      return m_Name;
     }

     //统一叶节点与非叶节点的print方法
     //这样就可以用先序遍历的方法来迭代叶子节点和非叶子节点了。
     virtual void PrintInformation() = 0;

     inline void setComponent(){
      m_bIsComponentOrLeaf = true;
     }

     inline bool isComponent(){
      return m_bIsComponentOrLeaf;
     }

     CComponent(string inName) : m_Name(inName),m_bIsComponentOrLeaf(false){}

     virtual ~CComponent() {}
    };

    //////////////////////////////////////////
    //在C++对象不像在Java中一样,有统一的基类Ojbect,
    //所以,我们在这里为所有要出售的对象设定一个对象的基类
    //重构修改: 这个是做为组合的新叶子结点
    class CBaseObjectMenuItem : public CComponent
    {
    private:
     float  m_Price;
    public:
     CBaseObjectMenuItem(string name, float price) : CComponent(name),  m_Price(price){}

     virtual float getPrice() {
      return m_Price;
     }
     
     virtual string getName(){
      return m_Name;
     }

     //这个已经没用了,可以注销掉
     /*
     void showInformation(){
      cout<<"Name : "<<m_Name<<"\t\t\t";
      cout<<"Price : "<<m_Price<<endl;
     }
     */

     void PrintInformation(){
      cout<<"Name : "<<m_Name<<"\t\t\t";
      cout<<"Price : "<<m_Price<<endl;
     }
    public:

    };

    ////////////////////////////////////////////
    //我这里先给出一个菜单类的接口,
    //这里简单处理菜单项,假设菜单项总是由一个名字和一个价格所决定的
    //重构修改: 这个是做为组合的新组合类
    //这里其实是一个递归式
    class CSaleMenu : public CComponent
    {
    public:
     virtual CIteratorInterface* createIterator() = 0;

    public:
     CSaleMenu(string inName) : CComponent(inName) {}

     //组合类的三个基本方法,只能由非叶子结点来操作
     //在这里例子中就是Menu

     void addComponent(CComponent* menuComponent) = 0;

     void removeComponent(CComponent* rmComponent) {
            //我在这里就不实现这个了,谁看这个程序的话,就帮我补充上去了吧
     }

     CComponent* getChild(unsigned long  i);

     virtual ~CSaleMenu(){ }

    public:
     //显示菜单
     //先序遍历菜单
     void PrintInformation();
    };

    /////////////////////////////////////////
    //电器菜单类,用数组实现
    //其中有一个方法可以用来创建一个迭代器
    class CElectricMenu : public CSaleMenu
    {
    private:
     unsigned long       m_totalItems;

     //我这里也用指针的原因是为了防止对象切片(slicing)
     //因为现在这里还是非常简单的情况就是,菜单对像还只是一个基类的直接对象
     //如果这个对象是一个菜单项基类的派生类的对象时,切记:千万不要以数组的多态方式来处理
     //这样会导致切片问题,可详见more efficient C++的相关章节
     CComponent* m_ItemPointers[MAX_ARRAY_SIZE];

    public:
     //组合方法重载
     void addComponent(CComponent* menuComponent){
      if(!menuComponent)
       return;
            m_ItemPointers[m_totalItems++] = menuComponent;
     }

    public:
     CElectricMenu(string inName) : CSaleMenu(inName), m_totalItems(0){}

     inline unsigned long getTotalMenuItems(){
      return m_totalItems;
     }

     CComponent** getMenu(){
      return m_ItemPointers;
     }

     CIteratorInterface* createIterator();


     ~CElectricMenu(){
      for(unsigned long i = 0; i < m_totalItems; i++)
       delete m_ItemPointers[i];
     }
    };

    /////////////////////////////////////////
    //食品菜单菜,用vector实现
    class CFoodMenu : public CSaleMenu
    {
    private:
     //我这里也用指针的原因是为了防止对象切片(slicing)
     //因为现在这里还是非常简单的情况就是,菜单对像还只是一个基类的直接对象
     //如果这个对象是一个菜单项基类的派生类的对象时,切记:千万不要以数组的多态方式来处理
     //这样会导致切片问题,可详见more efficient C++的相关章节

     //这里把CBaseObjectMenuItem改为CComponent
     vector<CComponent*> m_ItemPointer;
    public:
     CFoodMenu(string inName) : CSaleMenu(inName){}

     //组合方法重载
     void addComponent(CComponent* menuComponent){
      if(!menuComponent)
       return;
      m_ItemPointer.push_back(menuComponent);
     }

     vector<CComponent*>& getMenu(){
      return m_ItemPointer;
     }

     CIteratorInterface* createIterator();

     ~CFoodMenu(){
      for(int i = 0; i < static_cast<int>(m_ItemPointer.size()); i++){
       delete m_ItemPointer[i];
      }
      m_ItemPointer.clear();
     }
    };

    /**********************************************************

    CIterator.h

    **********************************************************/

    #pragma once
    #include <iostream>
    #include <string>
    #include <vector>
    #include "baseobject.h"
    using namespace std;

    class CBaseObjectMenuItem;
    class CComponent;

    //在原有的基础上,把这里所有MenuItem的基类改为CComponent

    //////////////////////////////////////////
    //迭代器的接口,这是每个迭代器都要实现的.
    class CIteratorInterface
    {
    public:
     virtual bool hasNext() = 0;
     virtual CComponent* next() = 0;
    };

    /////////////////////////////////////////
    //这是一个用于数组接口的迭代器,我们实现了电器数组的迭代
    //用数据实现
    class CElectricIterator : public CIteratorInterface
    {
    private:
     unsigned long m_totalItems;
     CComponent** m_MenuItem;

     unsigned long m_currentIt;

    public:
     CElectricIterator(CComponent** inMenu, unsigned long inTI);

     bool hasNext();

     CComponent* next();
    };

    /////////////////////////////////////////
    //这是一个用于vector接口的迭代器,我们实现了食物vector的迭代
    //用vector实现
    class CFoodIterator : public CIteratorInterface
    {
    private:
     vector<CComponent*>& m_MenuItem;

     unsigned long m_currentIt;
    public:
     CFoodIterator(vector<CComponent*>& inItemList);

     bool hasNext();

     CComponent* next();
    };

    /********************************************************

    CMenuShower.h

    ********************************************************/

    #pragma once
    #include <iostream>
    #include "../组合模式/baseobject.h"
    #pragma once
    #include "../组合模式/CIterator.h"
    #include "../组合模式/baseobject.h"


    //////////////////////////////////////////////
    //这个客户类就和原先完全不一样了
    //现在这个客户类有唯一的一个component
    class CMenuShower2
    {
    private:
     CComponent* m_component;
    public:
     CMenuShower2(CComponent* rootComponent) :
       m_component(rootComponent)
       {

       }

        void PrintMenu()
     {
      m_component->PrintInformation();
     }
     
    };

    /*********************************************************

    baseobject.cpp

    *********************************************************/

    #include "baseobject.h"

    #include "CIterator.h"


    CIteratorInterface* CElectricMenu::createIterator()
    {
     //注意这里new了一个iterator
     //在调用的地方,调用完了之后,一定要把这块内存释放掉。
     return static_cast<CIteratorInterface*>(
      new CElectricIterator(m_ItemPointers, m_totalItems));
    }


    CIteratorInterface* CFoodMenu::createIterator()
    {
     //注意这里new了一个iterator
     //在调用的地方,调用完了之后,一定要把这块内存释放掉。
     return static_cast<CIteratorInterface*>(
      new CFoodIterator(m_ItemPointer));
    }


    void CSaleMenu::PrintInformation()
    {
     //这里我在组合模式中,嵌套了迭代器模式
     CIteratorInterface* it = createIterator();
     while(it->hasNext())
     {
      CComponent*bmi = (CComponent*)it->next();
      if(bmi)
       bmi->PrintInformation();
     }

     delete it;
    }

    CComponent* CSaleMenu::getChild(unsigned long i)
    {
     unsigned long tpidx = 0;
     CIteratorInterface* it = createIterator();
     while(it->hasNext())
     {
      if(tpidx++ == i)
       return static_cast<CComponent*>(it->next());
     }
     delete it;
     return NULL;
    }

    /***********************************************************

    Citerator.cpp

    ***********************************************************/

    #include "CIterator.h"
    #include "baseobject.h"

    /////////////////////////////////////////
    //这是一个用于数组接口的迭代器,我们实现了电器数组的迭代
    //用数据实现
    //实现部分
    CElectricIterator::CElectricIterator(CComponent** inMenu, unsigned long inTI)
    : m_MenuItem(inMenu), m_totalItems(inTI), m_currentIt(0){

    }

    bool CElectricIterator::hasNext()
    {
     return m_currentIt < m_totalItems ? true : false;
    }

    CComponent* CElectricIterator::next(){
     if(m_currentIt < m_totalItems)
      return m_MenuItem[m_currentIt++];
     return NULL;
    }


    /////////////////////////////////////////
    //这是一个用于vector接口的迭代器,我们实现了食物vector的迭代
    //用vector实现
    //实现部分
    CFoodIterator::CFoodIterator(vector<CComponent*>& inItemList)
    : m_MenuItem(inItemList), m_currentIt(0){

    }

    bool CFoodIterator::hasNext()
    {
     return m_currentIt < m_MenuItem.size() ? true : false;
    }

    CComponent* CFoodIterator::next(){
     if(m_currentIt < m_MenuItem.size())
      return m_MenuItem[m_currentIt++];
     return NULL;
    }

    /***********************************************************

    testCompositionPattern.cpp

    ***********************************************************/

    /*
       设计模式; 组合模式

       允许我们将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
       组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象。(比如说,实现子菜单。)
       使用组合结构,我们能把相同的操作应用在组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。
       组件与组合:
       组合包含组件。组件有两种:组合与叶节点元素。(递归?不是吗。)。组合持有一群孩子,这些孩子可以是别的组合或者叶节点元素。
       当我们用这种方式组织数据的时候,最终会得到树形结构。
       类结构说明:客户拥有一个Component来操作组合中的对象。
       Component为组合为的所有对象定义一个接口,不管是组合还是叶节点(Component可以为add(),remove(), getchild()和它的操作实现一些默认的行为)。
       叶节点没有孩子,它通过实现Composite支持的操作,定义了组合内的元素的行为。Composite的角色是要定义组件的行为,而这样的组件具有子节点。(见类图)

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    这个程序要做的是:
        对迭代器模式的那个程序进行重构,从而实现子菜单的功能。
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

       by 何戬, hejian@cad.zju.edu.cn
    */
    #include <iostream>
    #include "../组合模式/CMenuShower.h"
    using namespace std;

    int main()
    {
     //根菜单
     CComponent* rootComponent = new CElectricMenu("电器菜单");

     CMenuShower2* menuShower = new CMenuShower2(rootComponent);

     //根菜单下有一个手机的子菜单
     CSaleMenu* cellphone = new CElectricMenu("电器菜单-手机");
     rootComponent->addComponent(cellphone);

     //手机菜单下有两款手机
     cellphone->addComponent(new CBaseObjectMenuItem("电器菜单-手机-诺基亚E71", 2300.0f));
     cellphone->addComponent(new CBaseObjectMenuItem("电器菜单-手机-诺基亚N79", 2120.0f));

     CSaleMenu* cellphoneApple = new CElectricMenu("电器菜单-手机");
     cellphoneApple->addComponent(new CBaseObjectMenuItem("电器菜单-手机-iPhone 3G", 4650.0f));
     cellphoneApple->addComponent(new CBaseObjectMenuItem("电器菜单-手机-iPhone 8G", 2100.0f));

     //再加一个子菜单
     cellphone->addComponent(cellphoneApple);

     //////////////////////////
     //现在我要在诺基亚E71下,细分成

     ////////////////////////////
     //加点零食主菜单
     CSaleMenu* fmenu = new CFoodMenu("零食菜单");

     CSaleMenu* fKFC = new CFoodMenu("KFC");
     fKFC->addComponent(new CBaseObjectMenuItem("KFC 1号套餐", 23.5f));
     fKFC->addComponent(new CBaseObjectMenuItem("KFC 2号套餐", 19.5f));
     fKFC->addComponent(new CBaseObjectMenuItem("KFC 3号套餐", 25.5f));

     rootComponent->addComponent(fmenu);
     //在我的这个程序里像以下这样做是不允许的.
     //一个菜单被加入到任何地方两次或以上
     //因为这样会导致同一个对象会被析构两次.
     //rootComponent->addComponent(fmenu)
     fmenu->addComponent(new CBaseObjectMenuItem("浙江大学靓园早饭", 2.5f));

     fmenu->addComponent(fKFC);


     //现在MC也来了//价格一样,东西不一样.
     CSaleMenu* fMcDn = new CFoodMenu("McD");
     fKFC->addComponent(new CBaseObjectMenuItem("Mc 1号套餐", 23.5f));
     fKFC->addComponent(new CBaseObjectMenuItem("Mc 2号套餐", 19.5f));
     fKFC->addComponent(new CBaseObjectMenuItem("Mc 3号套餐", 25.5f));

     fmenu->addComponent(fMcDn);


     //////////////////////////////////////
     //现在又有索爱的手机来了.
     CSaleMenu* saPhone = new CElectricMenu("电器菜单-手机-索爱");
     saPhone->addComponent(new CBaseObjectMenuItem("电器菜单-手机-索爱p900i", 1600.0f));
     saPhone->addComponent(new CBaseObjectMenuItem("电器菜单-手机-索爱p660i", 800.0f));
     saPhone->addComponent(new CBaseObjectMenuItem("电器菜单-手机-索爱K900c", 1200.0f));

     cellphone->addComponent(saPhone);

     menuShower->PrintMenu();
     delete menuShower;
     delete rootComponent;
     return 0;
    }

  • 相关阅读:
    JobHistory搜索智能化
    JobHistory搜索智能化
    JobHistory搜索智能化
    Hadoop Ls命令增加显示条数限制参数
    Hadoop Ls命令增加显示条数限制参数
    Markdown的简单用法
    Markdown常用编辑器
    搜索引擎的使用
    avalon.js 文字显示更多与收起
    浏览器访问网页的详细内部过程
  • 原文地址:https://www.cnblogs.com/skyofbitbit/p/2756482.html
Copyright © 2011-2022 走看看