序: 组合模式在实际开发中实在是使用的很频繁,很实用.
抽象场景:
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,
但是在用户的角度来说也只是一个上传文件的任务而已,它是不知道打包的过程的,所以我们还需要把两个任务打包为一个任务再执行.在显示进度条时能正确显示表面的任务数.
AsyncTaskPackageAsyncTask
#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这种组合产品,组合定价.
转载保留原创地址: http://blog.csdn.net/infoworld/article/details/48423669
参考:
《设计模式-可复用面向对象软件的基础》