基于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;
}

运行结果:

五.总结

  • 组合模式允许客户统一地处理组合和个别对象。
  • 组合结构内的任意对象称为组件。组件可以是其他组合或者叶子。
  • 实现组合模式有许多设计上的折中。你要根据需要平衡透明和安全。
相关推荐
_哆啦A梦1 天前
Vibe Coding 全栈专业名词清单|设计模式·基础篇(创建型+结构型核心名词)
前端·设计模式·vibecoding
端平入洛2 天前
delete又未完全delete
c++
端平入洛3 天前
auto有时不auto
c++
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
哇哈哈20214 天前
信号量和信号
linux·c++
多恩Stone4 天前
【C++入门扫盲1】C++ 与 Python:类型、编译器/解释器与 CPU 的关系
开发语言·c++·人工智能·python·算法·3d·aigc
starlaky4 天前
Django入门笔记
笔记·django
勇气要爆发4 天前
吴恩达《LangChain LLM 应用开发精读笔记》1-Introduction_介绍
笔记·langchain·吴恩达
蜡笔小马4 天前
21.Boost.Geometry disjoint、distance、envelope、equals、expand和for_each算法接口详解
c++·算法·boost
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习