一、模式动机
类似菜单,里面不仅菜单项,还要包含子菜单。需要实现这种多级菜单 。
二、模式定义
组合模式(Composite Pattern)允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能 让客户以一致的方式处理个别对象以及对象组合。UML图如下:
三、模式示例
现在对象村餐厅想在菜单里,餐厅菜单里添加甜点菜单。也就是不仅仅要支持多个菜单,甚至还要支持菜单里的菜单。
C++代码实现:
#include <string>
#include <vector>
#include <iostream>
using namespace std;
//父类组件
class MenuComponent
{
public:
MenuComponent();
~MenuComponent();
virtual string getName() { throw std::exception("ERROR"); }
virtual string getDescription() { throw std::exception("ERROR"); }
virtual double getPrice() { throw std::exception("ERROR"); }
virtual bool isVegetarian() { throw std::exception("ERROR"); }
virtual void print() { throw std::exception("ERROR"); }
virtual void add(MenuComponent* component) { throw std::exception("ERROR"); }
virtual void remove(MenuComponent* component) { throw std::exception("ERROR"); }
virtual MenuComponent* getChild(int index) { throw std::exception("ERROR"); }
private:
};
MenuComponent::MenuComponent()
{
}
MenuComponent::~MenuComponent()
{
}
//迭代器基类
class Iterator
{
public:
//是否有下一个菜单
virtual bool hasNext() = 0;
//取下一个菜单
virtual MenuComponent* next() = 0;
};
class NullIterator :public Iterator
{
public:
NullIterator();
~NullIterator();
MenuComponent* next()
{
return NULL;
}
bool hasNext()
{
return false;
}
};
NullIterator::NullIterator()
{
}
NullIterator::~NullIterator()
{
}
//煎饼屋餐单迭代器
class CompositeIterator : public Iterator
{
public:
CompositeIterator(vector<MenuComponent*> item)
{
items = item;
iter = items.begin();
}
MenuComponent* next()
{
MenuComponent* menuItem = *iter;
++iter;
return menuItem;
}
bool hasNext()
{
if (iter == items.end())
{
return false;
}
else
{
return true;
}
}
private:
vector<MenuComponent*> items;
vector<MenuComponent*>::const_iterator iter;
};
//菜单项
class MenuItem : public MenuComponent
{
public:
MenuItem()
{
}
MenuItem(string name, string description, bool vegetarian, double price)
{
name_ = name;
description_ = description;
vegetarian_ = vegetarian;
price_ = price;
}
~MenuItem()
{
}
string getName() { return name_; }
string getDescription() { return description_; }
bool isVegetarian() { return vegetarian_; }
double getPrice() { return price_; }
void print()
{
cout << getName() << endl;
cout << getDescription() << endl;
cout << getPrice() << endl;
}
void add(MenuComponent* component)
{
}
void remove(MenuComponent* component)
{
}
MenuComponent* getChild(int index)
{
return NULL;
}
Iterator* createIterator(){ return new NullIterator; }
private:
string name_;
string description_;
bool vegetarian_;
double price_;
};
//餐单基类
class Menu : public MenuComponent
{
public:
Menu(string name, string description);
string getName() { return name_; }
string getDescription() { return description_; }
MenuComponent* getChild(int index)
{
return menu_compents_[index];
}
void print()
{
cout << getName() << endl;
cout << getDescription() << endl;
Iterator* iterator = createIterator();
while (iterator->hasNext())
{
MenuComponent* menuComponet = iterator->next();
menuComponet->print();
}
}
vector<MenuComponent*> GetMenuItems() { return menu_compents_; }
Iterator* createIterator(){ return new CompositeIterator(this->GetMenuItems()); }
void add(MenuComponent* component)
{
menu_compents_.push_back(component);
}
protected:
vector<MenuComponent*> menu_compents_;
private:
string name_;
string description_;
};
Menu::Menu(string name, string description)
:name_(name),
description_(description)
{
}
//煎饼屋菜单
class PancakeHouseMenu :public Menu
{
public:
PancakeHouseMenu(string name, string description);
~PancakeHouseMenu();
void AddItem(string name, string description, bool vegetarian, double price)
{
MenuItem* menuItem = new MenuItem(name, description, vegetarian, price);
add(menuItem);
}
private:
};
PancakeHouseMenu::PancakeHouseMenu(string name, string description)
:Menu(name, description)
{
AddItem("K&B's Pancake Breakfast", "Pancakes with scrambled eggs, and toast", true, 2.99);
AddItem("Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", false, 2.99);
AddItem("Blueberry Pancakes", "Pancakes made with fresh blueberries", true, 3.49);
AddItem("Waffles", "with you choice of blueberries or strawberries", true, 3.59);
}
PancakeHouseMenu::~PancakeHouseMenu()
{
}
class DinerMenu :public Menu
{
public:
DinerMenu(string name, string description);
~DinerMenu();
void AddItem(string name, string description, bool vegetarian, double price)
{
MenuItem* menuItem = new MenuItem(name, description, vegetarian, price);
menu_compents_.push_back(menuItem);
}
private:
};
DinerMenu::DinerMenu(string name, string description)
:Menu(name, description)
{
AddItem("Vegetarian BLT", "(Fakin) Bacon with lettuce & tomato on whole wheat", true, 2.99);
AddItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99);
AddItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29);
AddItem("Hotdog", "a hot dog, with saurkraut,relish,onions,topped with cheese", false, 3.05);
}
DinerMenu::~DinerMenu()
{
}
class DessertMenu : public Menu
{
public:
DessertMenu(string name, string description);
~DessertMenu();
void AddItem(string name, string description, bool vegetarian, double price)
{
MenuItem* menuItem = new MenuItem(name, description, vegetarian, price);
menu_compents_.push_back(menuItem);
}
private:
};
DessertMenu::DessertMenu(string name, string description)
:Menu(name, description)
{
AddItem("Apple Pie", "Apple pie with a flakey crust, topped with vanilla ice cream", true, 1.59);
}
DessertMenu::~DessertMenu()
{
}
class Waitess
{
public:
Waitess(MenuComponent* allMenus);
~Waitess();
void printMenu()
{
all_menu_->print();
}
private:
MenuComponent* all_menu_;
};
Waitess::Waitess(MenuComponent* allMenus)
:all_menu_(allMenus)
{
}
Waitess::~Waitess()
{
}
int _tmain(int argc, _TCHAR* argv[])
{
PancakeHouseMenu* pancakeHouseMenu = new PancakeHouseMenu("PancakeHouseMenu", "PancakeHouseMenu");
DinerMenu* dinerMenu = new DinerMenu("DinerMenu","DinerMenu");
DessertMenu* dessertMenu = new DessertMenu("DessertMenu", "DessertMenu");
dinerMenu->add(dessertMenu);
MenuComponent* allMenus = new Menu("AllMenus", "AllMenus");
allMenus->add(pancakeHouseMenu);
allMenus->add(dinerMenu);
Waitess waitess(allMenus);
waitess.printMenu();
system("pause");
return 0;
}
运行结果:
四、分析总结
组合模式提供一个结构,可同时包容个别对象和组合对象。组合模式允许客户对个别对象和组合对象一视同仁。组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点。