[设计模式]_[中级]_[组合模式(Composite)分析与总结]
2015-09-13 23:43
381 查看
序: 组合模式在实际开发中实在是使用的很频繁,很实用.
-- 如果想对某些对象打包统一处理时,比如用std::vector.
2. 你希望用户忽略组合对象与单个对象的不同, 用户将统一地使用组合结构中的所有对象.[原书]
-- 如果想把这个打包对象和被打包的对象统一处理时,即提供一致的接口,也就是他们其实可以是
父类(一般作为组合对
象使用)-子类(一般作为基本对象使用)的关系.
象,而这个组合对象又可以被组合,这样不断的递归下去. [原书]
如有不认识的UML线参考这里:
http://blog.csdn.net/ai92/article/details/202606
2. 组合对象是继承基本对象的容器,即它可以容纳多个基本对象,它定义了管理基本对象的方法,比如Add,Remove.
3. 基本对象定义了基本的操作接口,它可以操作所有的组合对象或有子类化的基本对象.
这里还有一个术语要理解:
基本对象,因为它不能作为容器,所以也可以称作子部件.
组合对象,因为它可以作为容器,容器里包含基本对象,所以也可以称做父部件.
2. 共享组件,这里意思是共享子部件,一般情况下一个子部件只有一个父部件,如果有多个父部件,
需要自己设计好子部分删除的方式.
3. 子部件是否需要实现容器的功能(也就是组合对象作为基本对象),这里不应该实现,因为没必要,还要多增加内存.
4. 子部件排序,至于用什么数据结构存储子部件,根据需求需要,比如如果需要快速查询子部件的,可以使用map,如果需要
快速访问和自定义排序的,可以使用vector.
5. 使用缓冲存储改善性能,比如查找时,可以把某些结果缓存,前提是子部件没变化时.
6. 应该由谁来删除子部件,很明显,我们直接参考树的结构,如果树干被砍断了,叶子也会掉下来的,即父部件负责子部件的生命周期.
即组合对象提供remove,add,析构函数等方法操作子部件.
但是在用户的角度来说也只是一个上传文件的任务而已,它是不知道打包的过程的,所以我们还需要把两个任务打包为一个任务再执行.在显示进度条时能正确显示表面的任务数.
AsyncTaskPackage
AsyncTask
输出:
还有其他例子:
比如套餐A,套餐B,打包为套餐AB这种组合产品,组合定价.
转载保留原创地址: /article/1562950.html
参考:
《设计模式-可复用面向对象软件的基础》
抽象场景:
1. 你想表示对象的部分-整体层次结构.[原书]-- 如果想对某些对象打包统一处理时,比如用std::vector.
2. 你希望用户忽略组合对象与单个对象的不同, 用户将统一地使用组合结构中的所有对象.[原书]
-- 如果想把这个打包对象和被打包的对象统一处理时,即提供一致的接口,也就是他们其实可以是
父类(一般作为组合对
象使用)-子类(一般作为基本对象使用)的关系.
模式定义:
关键点:
定义了包含基本对象和组合对象的类层次结构 基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去. [原书]
如有不认识的UML线参考这里:
http://blog.csdn.net/ai92/article/details/202606
必须要求:
1. 他们的结构是子类和父类的类层次关系。2. 组合对象是继承基本对象的容器,即它可以容纳多个基本对象,它定义了管理基本对象的方法,比如Add,Remove.
3. 基本对象定义了基本的操作接口,它可以操作所有的组合对象或有子类化的基本对象.
这里还有一个术语要理解:
基本对象,因为它不能作为容器,所以也可以称作子部件.
组合对象,因为它可以作为容器,容器里包含基本对象,所以也可以称做父部件.
特殊要求:
1. 显式的父部件引用,即基本对象里要有对组合对象的引用,这样可以传递信息给父部件!2. 共享组件,这里意思是共享子部件,一般情况下一个子部件只有一个父部件,如果有多个父部件,
需要自己设计好子部分删除的方式.
3. 子部件是否需要实现容器的功能(也就是组合对象作为基本对象),这里不应该实现,因为没必要,还要多增加内存.
4. 子部件排序,至于用什么数据结构存储子部件,根据需求需要,比如如果需要快速查询子部件的,可以使用map,如果需要
快速访问和自定义排序的,可以使用vector.
5. 使用缓冲存储改善性能,比如查找时,可以把某些结果缓存,前提是子部件没变化时.
6. 应该由谁来删除子部件,很明显,我们直接参考树的结构,如果树干被砍断了,叶子也会掉下来的,即父部件负责子部件的生命周期.
即组合对象提供remove,add,析构函数等方法操作子部件.
相关模式:
--- 装饰者模式,它具备组合模式的必须要求的第1,3点.但是不具备容器的功能,它更多的是封装基本对象提供过滤行为.例子1:
在执行任务时,比如下载文件,解压文件等,通过AsyncTask对任务进行封装便于统计和统一处理,但是也有一些任务是打包的,比如上传文件,需要先压缩后上传两个AsyncTask,但是在用户的角度来说也只是一个上传文件的任务而已,它是不知道打包的过程的,所以我们还需要把两个任务打包为一个任务再执行.在显示进度条时能正确显示表面的任务数.
AsyncTaskPackage
AsyncTask
#include <iostream> #include <stdint.h> #include <vector> #include <Windows.h> using namespace std; typedef void (*TaskQueueCommand)(void* interTask); enum TaskStatus { kTaskStatusUnDo = 0, kTaskStatusFinish, kTaskStatusDoing, kTaskStatusAbort, kTaskStatusFail }; class TaskPackage; class Task { public: Task() { interFunc = NULL; interArgs = NULL; taskPackage_ = NULL; status_ = kTaskStatusUnDo; } virtual ~Task(){} int64_t id_; // 执行当前任务 virtual void Exec() { interFunc(interArgs); } // 通知任务处理完 void Notify(); void Cancel() { status_ = kTaskStatusAbort; } int32_t status_; void* interArgs; TaskQueueCommand interFunc; TaskPackage* taskPackage_; }; class TaskPackage : public Task { public: TaskPackage() { taskIndex_ = -1; isContinueIfFail = false; } ~TaskPackage() { for (int i = 0; i < queue_.size(); ++i) { delete queue_[i]; } } std::vector<Task*> queue_; int32_t GetTaskCount() { return queue_.size(); } void AddTask(Task* task) { task->taskPackage_ = this; queue_.push_back(task); } void RecvMessage(Task* task) { //这里的逻辑是判断是否需要继续执行 switch(task->status_) { case kTaskStatusFinish: { Exec(); break; } case kTaskStatusFail: { if (isContinueIfFail) { Exec(); }else { status_ = kTaskStatusFail; } break; } } } void Exec() { if(taskIndex_+1 < queue_.size()) { ++taskIndex_; queue_[taskIndex_]->Exec(); }else { status_ = kTaskStatusFinish; if(taskPackage_) { taskPackage_->Exec(); } } } int64_t Cancel() { // 取消当前任务 Task* task = queue_[taskIndex_]; task->status_ = kTaskStatusAbort; // 取消Package status_ = kTaskStatusAbort; // 取消父Package if (taskPackage_) { taskPackage_->Cancel(); } } bool isContinueIfFail; private: int32_t taskIndex_; }; void Task::Notify() { if(taskPackage_) { taskPackage_->RecvMessage(this); } } static int64_t GetNewTaskId() { static int64_t gId = 0; return ++gId; } // 注意,这个interTask可以是自定义类型,这里为了省事传Task作为参数. static void PTaskQueueCommand(void* interTask) { Task* task = (Task*)interTask; std::cout << "id: " << task->id_ << std::endl; Sleep(1000); //要找到当前的任务并调用Notify才能继续执行. task->status_ = kTaskStatusFinish; task->Notify(); } int main(int argc, char const *argv[]) { std::cout << "==================================" << std::endl; TaskPackage* tp = new TaskPackage(); Task* task = new Task(); task->id_ = GetNewTaskId(); task->interFunc = PTaskQueueCommand; task->interArgs = task; tp->AddTask(task); task = new Task(); task->id_ = GetNewTaskId(); task->interFunc = PTaskQueueCommand; task->interArgs = task; tp->AddTask(task); //1.第2个Package TaskPackage* tp2 = new TaskPackage(); task = new Task(); task->id_ = GetNewTaskId(); task->interFunc = PTaskQueueCommand; task->interArgs = task; tp2->AddTask(task); task = new Task(); task->id_ = GetNewTaskId(); task->interFunc = PTaskQueueCommand; task->interArgs = task; tp2->AddTask(task); tp->AddTask(tp2); std::cout << "GetTaskCount(): " << tp->GetTaskCount() << std::endl; tp->Exec(); return 0; }
输出:
================================== GetTaskCount(): 3 id: 1 id: 2 id: 3 id: 4
还有其他例子:
比如套餐A,套餐B,打包为套餐AB这种组合产品,组合定价.
转载保留原创地址: /article/1562950.html
参考:
《设计模式-可复用面向对象软件的基础》
相关文章推荐
- Web后台模拟前端post(带NTLM验证)
- [Shell]正则表达式与通配符
- 饼图,扇形,柱状图
- C程序设计 习题之1-20 detab
- nginx index priority重置和改名方法
- 好书推荐-《Head First设计模式》
- Redis的安装
- clipdrawable
- 红魔灭红军
- Bypass WAF Cookbook
- RPM及YUM的使用
- C++故障排除-VS2010 error LNK2019: unresolved external symbol _WinMain@16 referenced in function ___tmai
- Web项目使用Spring框架服务器启动加载xml文件的过程学习
- NYOJ 236 心急的C小加 【贪心】
- Scramble String
- PopupWindow与PopupMenu的用法
- 调整割边的流量使网络通过c流量 网络流 网络扩容 uva 11248
- 设计模式:适配器模式
- Matlab2014下载和破解方法,以及Matlab很好的学习网站
- Oracle 学习之 数据仓库(二) Dimension 的理解