设计模式(C++)-结构型模式-组合模式

设计模式(C++)-结构型模式-组合模式

一、组合模式概述

组合模式是一种结构型设计模式,允许你将对象组合成树形结构来表示"部分-整体"的层次结构。它让客户端能以统一的方式处理单个对象和对象的组合。

核心思想:一致性处理叶子与容器。无论处理单个对象还是组合对象,客户端都使用相同的接口。

二、组合模式UML类图

场景:文件系统浏览器

模拟一个树形文件系统,其中文件和目录都可以被统一处理。

三、代码实现

cpp 复制代码
//Composite.h
/*
组合模式(composite pattern)是一种结构型设计模式,允许你将对象组合成树形结构来
表示"部分-整体"的层次结构。它让客户端能以统一的方式处理单个对象和对象的组合。
核心思想:一致性处理叶子与容器。无论处理单个对象还是组合对象,客户端都使用相同的接口。
*/
#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <vector>
using namespace std;

//==============抽象组件=====================
class FileSystemComponent {
protected:
	string name_;
public:
	explicit FileSystemComponent(const string&name) :name_(name) {}
	virtual ~FileSystemComponent() = default;

	//核心操作
	virtual void display(int depth = 0)const = 0;
	virtual int getSize()const = 0;

	//组件管理(叶子节点可抛出异常或空实现)
	virtual void add(shared_ptr<FileSystemComponent>component);
	virtual void remove(shared_ptr<FileSystemComponent>component);
	virtual shared_ptr<FileSystemComponent> getChild(int index);
	virtual int getChildCount()const;
	string getName()const;
	virtual bool isDirectory()const;
	//格式化工具
	static string indent(int depth);
	static string formatSize(int bytes);
};
//叶子组件:文件
class File :public FileSystemComponent {
private:
	int size_; //文件大小(字节)
	string extension_;
public:
	File(const std::string& name, int size)
		: FileSystemComponent(name), size_(size) {
		// 提取扩展名
		size_t dotPos = name.find_last_of('.');
		if (dotPos != std::string::npos) {
			extension_ = name.substr(dotPos + 1);
		}
	}
	int getSize()const override;
	string getExtension()const;
	void display(int depth = 0)const override;
};

//符合组件:目录
class Directory :public FileSystemComponent {
private:
	vector<shared_ptr<FileSystemComponent>>children_;
public:
	explicit Directory(const std::string&name) :FileSystemComponent(name) {}
	//重写组件管理方法
	void add(shared_ptr<FileSystemComponent>component)override;
	void remove(shared_ptr<FileSystemComponent>component)override;
	shared_ptr<FileSystemComponent> getChild(int index)override;
	int getChildCount()const override;
	bool isDirectory()const override;
	int getSize()const override;
	void display(int depth = 0)const override;
	shared_ptr<FileSystemComponent> find(const string&name);
	int countFiles()const;
	int countDirectories()const;
};
void testComposite();
//composite.cc
#include "composite.h"
//==============抽象组件=====================
void FileSystemComponent::add(shared_ptr<FileSystemComponent>component) {
	throw std::runtime_error("不支持添加操作");
}
void FileSystemComponent::remove(shared_ptr<FileSystemComponent>component) {
	throw std::runtime_error("不支持删除操作");
}
shared_ptr<FileSystemComponent> FileSystemComponent::getChild(int index) {
	throw std::runtime_error("无子组件");
}
int FileSystemComponent::getChildCount()const {
	return 0;
}
string FileSystemComponent::getName()const {
	return name_;
}
bool FileSystemComponent::isDirectory()const {
	return false;
}
//格式化工具
string FileSystemComponent::indent(int depth) {
	return std::string(depth * 2, ' ');
}
string FileSystemComponent::formatSize(int bytes) {
	const char* units[] = { "B", "KB", "MB", "GB" };
	double size = bytes;
	int unitIndex = 0;

	while (size >= 1024 && unitIndex < 3) {
		size /= 1024;
		unitIndex++;
	}

	char buffer[32];
	snprintf(buffer, sizeof(buffer), "%.1f %s", size, units[unitIndex]);
	return buffer;
}
//叶子组件:文件
int File::getSize()const {
	return size_;
}
string File::getExtension()const {
	return extension_;
}
void File::display(int depth)const {
	cout << indent(depth)<< "📄 " << name_<< " [" << formatSize(size_) << "]"
	<< " (" << extension_ << ")"<< std::endl;
}

//复合组件:目录
//重写组件管理方法
void Directory::add(shared_ptr<FileSystemComponent>component) {
	children_.push_back(component);
}
void Directory::remove(shared_ptr<FileSystemComponent>component) {
	auto it = std::find(children_.begin(), children_.end(), component);
	if (it != children_.end()) {
		children_.erase(it);
	}
}
shared_ptr<FileSystemComponent> Directory::getChild(int index) {
	if (index >= 0 && index < static_cast<int>(children_.size())) {
		return children_[index];
	}
	return nullptr;
}
int Directory::getChildCount()const {
	return static_cast<int>(children_.size());
}
bool Directory::isDirectory()const {
	return true;
}
int Directory::getSize()const {
	int totalSize = 0;
	for (const auto& child : children_) {
		totalSize += child->getSize();
	}
	return totalSize;
}
void Directory::display(int depth)const {
	// 显示当前目录
	cout << indent(depth)
		<< "📁 " << name_
		<< " [" << getChildCount() << " items, "
		<< formatSize(getSize()) << "]"
		<< std::endl;

	// 递归显示子组件
	for (const auto& child : children_) {
		child->display(depth + 1);
	}
}
shared_ptr<FileSystemComponent>Directory::find(const string&name) {
	for (const auto& child : children_) {
		if (child->getName() == name) {
			return child;
		}
		// 如果是目录,递归查找
		if (auto dir = std::dynamic_pointer_cast<Directory>(child)) {
			auto result = dir->find(name);
			if (result) return result;
		}
	}
	return nullptr;
}
int Directory::countFiles()const {
	int count = 0;
	for (const auto& child : children_) {
		if (!child->isDirectory()) {
			count++;
		}
		else {
			if (auto dir = std::dynamic_pointer_cast<Directory>(child)) {
				count += dir->countFiles();
			}
		}
	}
	return count;
}
int Directory::countDirectories()const {
	int count = 0;
	for (const auto& child : children_) {
		if (child->isDirectory()) {
			count++;
			if (auto dir = std::dynamic_pointer_cast<Directory>(child)) {
				count += dir->countDirectories();
			}
		}
	}
	return count;
}
void testComposite() {
	cout << "=================Composite start===============" << endl;
	cout << "🌳 文件系统浏览器 - 组合模式演示\n";
	cout << "================================\n\n";

	//创建文件
	auto file1 = std::make_shared<File>("readme.txt", 2048);
	auto file2 = std::make_shared<File>("main.cpp", 4096);
	auto file3 = std::make_shared<File>("image.jpg", 1024 * 1024 * 2);  // 2MB
	auto file4 = std::make_shared<File>("config.json", 512);
	auto file5 = std::make_shared<File>("video.mp4", 1024 * 1024 * 100);  // 100MB

	//创建目录结构
	auto root = std::make_shared<Directory>("/");
	auto documents = std::make_shared<Directory>("Documents");
	auto pictures = std::make_shared<Directory>("Pictures");
	auto videos = std::make_shared<Directory>("Videos");
	auto src = std::make_shared<Directory>("src");

	// 构建树形结构
	documents->add(file1);
	documents->add(file4);

	pictures->add(file3);

	videos->add(file5);

	src->add(file2);

	// 添加到根目录
	root->add(documents);
	root->add(pictures);
	root->add(videos);
	root->add(src);

	// 显示整个文件系统
	cout << "📁 完整文件系统结构:\n";
	root->display();

	cout << "\n📊 统计信息:\n";
	cout << "文件总数: " << root->countFiles() << "\n";
	cout << "目录总数: " << root->countDirectories() << "\n";
	cout << "总大小: " << FileSystemComponent::formatSize(root->getSize()) << "\n";

	// 搜索文件
	cout << "\n🔍 搜索文件:\n";
	auto found = root->find("main.cpp");
	if (found) {
		cout << "找到: ";
		found->display(0);
	}

	// 统一处理组件
	cout << "\n🔄 统一处理所有组件:\n";
	vector<std::shared_ptr<FileSystemComponent>> components = {
		root, documents, file1, src, file2
	};

	for (const auto& comp : components) {
		std::cout << "- " << comp->getName()
			<< " (" << (comp->isDirectory() ? "目录" : "文件") << ")"
			<< " 大小: " << FileSystemComponent::formatSize(comp->getSize())
			<< std::endl;
	}

	// 动态添加新文件
	std::cout << "\n➕ 动态添加新文件:\n";
	auto newFile = std::make_shared<File>("newfile.txt", 1024);
	documents->add(newFile);
	documents->display(1);
	cout << "=================Composite end===============" << endl;
}

四、优缺点总结

优点:

  • 统一处理:客户端代码简洁,无需判断节点类型
  • 简化客户端:客户端可统一处理简单和复杂元素
  • 易于扩展:添加新组件类型不影响现有代码
  • 树形结构:天然支持递归结构

缺点:

  • 过度泛化:某些操作对于叶子节点无意义
  • 类型检查困难:运行时才能发现无效操作
  • 性能开销:递归遍历可能较慢
  • 设计复杂:需仔细设计接口避免误用
相关推荐
青瓦梦滋1 小时前
Linux线程的同步与互斥
linux·c++
南境十里·墨染春水2 小时前
C++流类库 文件流操作
开发语言·c++
C++ 老炮儿的技术栈2 小时前
工业视觉检测:用 C++ 和 Snap7 库快速读写西门子 S7-1200
c语言·c++·git·qt·系统架构·visual studio·snap
橙子也要努力变强2 小时前
信号捕捉的底层机制-内核态和用户态初识
linux·服务器·c++
澈2072 小时前
内存四区模型详解(栈、堆、全局、常量)
c++·面试·职场和发展
橙子也要努力变强2 小时前
信号捕捉底层机制-进程与OS
linux·服务器·c++
青瓦梦滋2 小时前
Linux线程
linux·运维·c++
小张成长计划..2 小时前
【C++】23:封装set和map
c++
满天星83035772 小时前
【Linux/多路复用】select
linux·运维·服务器·c语言·c++