基于C++的《Head First设计模式》笔记——组合模式

目录

一.专栏简介

二.背景

三.定义组合模式

四.用组合设计和实现菜单

五.总结


一.专栏简介

本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。

本章将开始组合模式的学习,是上一篇迭代器模式的后传。

二.背景

书接上回,对象村打算在Diner Menu菜单中添加一个子菜单,我们同样需要支持客户方便地遍历所有菜单。我们就需要用到组合模式。

三.定义组合模式

组合模式允许我们将对象组合成树形结构来表现部分-整体层次结构。组合让客户可以统一处理个别对象和对象组合。

这个模式让我们创建树形结构,嵌套的菜单和菜单项组可以用同一结构来处理。通过把菜单和菜单项放进同一结构,我们创建了一个部分-整体层次,即一棵对象树,它由部件(菜单和菜单项)组成,却可以当成一个整体对待。

类图如下:

四.用组合设计和实现菜单

因此,我们如何把组合模式应用到我们的菜单?一开始,我们需要创建一个组件接口,该接口作为菜单和菜单项的通用接口,允许我们统一地对待它们。换句话说,我们可以在菜单或菜单项中调用同样的方法。

现在,调用菜单或菜单项上的某些方法可能没有意义,但我们可以处理,只需等一会。目前,我们快速浏览一下菜单如何符合组合模式结构:

下面,我们用代码实现,代码如下:

Composite.h:

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class MenuComponent
{
public:
	virtual void add(MenuComponent* menuComponent) { cerr << "Menu can not add" << endl; }
	virtual void remove(MenuComponent* menuComponent) { cerr << "Menu can not remove" << endl; }
	virtual MenuComponent* getChild(int i) { cerr << "Menu can not getChild" << endl; return nullptr; }
	virtual const string& getName() const { cerr << "MenuItem can not getName" << endl; return nullStr; }
	virtual const string& getDescription() const { cerr << "MenuItem can not getDescription" << endl; return nullStr; }
	virtual const double getPrice() const { cerr << "MenuItem can not getPrice" << endl; return -1; }
	virtual bool isVegeTarian() const { cerr << "MenuItem can not isVegeTarian" << endl; return false; }
	virtual void print() const = 0 {}
	bool operator==(const MenuComponent* another) { return this == another; }
private:
	string nullStr;
};

class MenuItem : public MenuComponent
{
public:
	MenuItem(const string& name, const string& description, bool vegetarian, double price):
		_name(name), _description(description), _vegetarian(vegetarian), _price(price)
	{}
	const string& getName() const override { return _name; }
	const string& getDescription() const override { return _description; }
	bool isVegeTarian() const override { return _vegetarian; }
	const double getPrice() const override { return _price; }
	void print() const override
	{
		cout << "菜单项:" << _name;
		if (_vegetarian) cout << "(v)";
		cout << ", " << _price << "      --" << _description << endl;
	}
private:
	string _name;
	string _description;
	bool _vegetarian;
	double _price;
};

class Menu : public MenuComponent
{
public:
	Menu(const string& name, const string& description):
		_name(name), _description(description)
	{}
	void add(MenuComponent* menuComponent) override { menuComponents.push_back(menuComponent); }
	void remove(MenuComponent* menuComponent) override
	{ 
		auto it = menuComponents.begin();
		while (it != menuComponents.end())
		{
			if (*it == menuComponent)
			{
				menuComponents.erase(it);
				return;
			}
			it++;
		}
	}
	MenuComponent* getChild(int i) override { return menuComponents[i]; }
	const string& getName() const override { return _name; }
	const string& getDescription() const override { return _description; }
	void print() const override
	{ 
		cout << endl << _name << ", " << _description << endl << "-------------------" << endl; 

		for (const auto& e : menuComponents)
		{
			e->print();
		}
	}
private:
	vector<MenuComponent*> menuComponents;
	string _name;
	string _description;
};

class Waitress
{
public:
	Waitress(MenuComponent* menuComponent):
		allMenus(menuComponent)
	{

	}
	void printMenu()
	{
		allMenus->print();
	}
private:
	MenuComponent* allMenus;
};

main.cpp:

cpp 复制代码
#include "Composite.h"

int main()
{
	MenuComponent* pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "BREAKFAST");
	MenuComponent* dinerMenu = new Menu("DINER MENU", "Lunch");
	MenuComponent* cafeMenu = new Menu("CAFE MENU", "Dinner");
	MenuComponent* dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");

	MenuComponent* allMenu = new Menu("ALL MENUS", "All menus combined");

	allMenu->add(pancakeHouseMenu);
	allMenu->add(dinerMenu);
	allMenu->add(cafeMenu);

	dinerMenu->add(new MenuItem("Pasta", "香啊啊啊啊啊啊", true, 3.89));
	dinerMenu->add(dessertMenu);
	dinerMenu->add(new MenuItem("Apple Pie", "香哈哈哈哈哈哈", true, 1.39));

	cafeMenu->add(new MenuItem("抹茶拿铁", "咖咖咖", false, 20));

	dessertMenu->add(new MenuItem("哈根达斯", "甜甜甜", false, 100));

	Waitress waitress(allMenu);
	waitress.printMenu();

	return 0;
}

运行结果:

五.总结

  • 组合模式允许客户统一地处理组合和个别对象。
  • 组合结构内的任意对象称为组件。组件可以是其他组合或者叶子。
  • 实现组合模式有许多设计上的折中。你要根据需要平衡透明和安全。
相关推荐
hssfscv2 小时前
Javaweb学习笔记——后端实战6登录功能1
笔记·后端·学习
山风wind2 小时前
设计模式-访问者模式详解
python·设计模式·访问者模式
Engineer邓祥浩2 小时前
设计模式学习(17) 23-15 访问者模式
学习·设计模式·访问者模式
ChoSeitaku2 小时前
28.C++进阶:map和set封装|insert|迭代器|[]
java·c++·算法
dgaf2 小时前
(2023-06-07) Win32API【1】-- DevC++做一个窗口
c++·windows·microsoft
jojo_zjx2 小时前
GESP 23年9月2级 小杨的X字矩阵
c++
君生我老2 小时前
C++ list类容器常用操作
开发语言·c++
fpcc2 小时前
跟我学C++中级篇——文件和目录
linux·c++
ouliten2 小时前
C++笔记:std::tuple
c++·笔记