设计模式之实现---组合模式
/******************************************************
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;
}