【设计模式——Composite模式】

Composite模式
计算机科学中广泛使用的数据结构是树结构,到处都可以找到树结构,例如,数据媒体(比如硬盘)上的文件系统,它的分层组织就符合树的结构。集成开发环境(IDE)的项目浏览器通常也具有树结构。在编译器设计中,用到一种叫作抽象语法树(AST)的方法,顾名思义,它是指以树状结构表示源代码的抽象语法结构,抽象语法树通常是编译器在语法分析阶段的结果。
对树状数据结构的面向对象的蓝图被称为组合模式。该模式的任务说明如下:
将对象组合成树结构来表示“部分——整体”的层次结构。组合允许客户端统一地处理单个对象和对象的组合。
我们在Command和Command处理器种的示例可以扩展为复合Command,并且Command还可以记录和重放。所以我们在之前的设计中添加了一个新类,一个CompositeCommand:

#pragma once
#include"Command.h"
#include<vector>

class CompositeCommand :public UndoableCommand
{
public:
	void addCommand(CommandPtr& command)
	{
		commands.push_back(command);
	}
	virtual void execute() override
	{
		for (const auto& command:commands)
		{
			command->execute();
		}
	}
	virtual void undo() override
	{
		for (const auto& command : commands)
		{
			command->undo();
		}
	}
private:
	std::vector<CommandPtr> commands;
};

CompositeCommand有一个成员函数addCommand(),它允许你将命令添加到CompositeCommand的实例。由于CompositeCommand类也实现了UndoableCommand接口,因此可以将其实例视为普通的command。换句话说,我们可以以其他的CompositeCommand来分层地组合出一个新的CompositeCommand。通过Composite模式的递归结构,你可以生成command树。
现在可以使用新添加的类CompositeCommand作为宏录制器,以便记录和重放command序列:

int main()
{
	CommandProcessor commandProcessor{};
	DrawingProcessor drawingProcessor{};
	auto macroRecorder = std::make_shared<CompositeCommand>();
	Point circleCenterPoint{ 20,20 };
	CommandPtr drawCircleCommand = std::make_shared<DrawingCircleCommand>(drawingProcessor,
		circleCenterPoint, 10);
	commandProcessor.execute(drawCircleCommand);
	macroRecorder->addCommand(drawCircleCommand);

	Point rectangleCenterPoint{ 30, 10 };
	CommandPtr drawRectangleCommand = std::make_shared<DrawRectangleCommand>(drawingProcessor,
		rectangleCenterPoint, 5, 8);
	commandProcessor.execute(drawRectangleCommand);
	macroRecorder->addCommand(drawRectangleCommand);
	commandProcessor.execute(macroRecorder);
	
	CommandPtr undoCommand = std::make_shared<UndoCommand>(commandProcessor);
	commandProcessor.execute(undoCommand);

	return 0;

在Composite模式的帮助下,现在我们很容易把简单的command组装成复杂的command序列(前者被称为“叶子”)。由于guancCompositeCommand还实现了UndoableCommand接口,因此它们可以像简单的Command一样被使用。这极大地简化了客户端的代码。
仔细观察会发现上面还有一个缺点。在使用类CompositeCommand的具体实例(macroRecorder)时才能访问成员函数addCommand(),而通过UndoableCommand接口无法使用该成员函数。换句话说,这里的组合类和叶子的地位并不平等(记住模式的意图)!
通用Composite模式中,管理子元素的函数是在抽象中声明的。于是在我们的例子中,这意味我们必须在接口UndoableCommand中声明一个addCommand()(顺便说一句,这将违反ISP)。这一致命的后果是叶子元素必须覆盖addCommand(),并且必须为这个成员函数提供有意义的实现。但这是不可能的!如果我们向DrawCircleCommand的实例添加一个Command,假使不违反最少惊讶原则,会出现什么问题呢?
如果这样做,那将违反里氏替换原则(LSP)。因此,对案例进行权衡并且区别对待组合类和叶子是较好的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值