[设计模式]_[中级]_[组合模式(Composite)分析与总结]


序: 组合模式在实际开发中实在是使用的很频繁,很实用.

抽象场景:

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这种组合产品,组合定价.



转载保留原创地址:   http://blog.csdn.net/infoworld/article/details/48423669

参考:

《设计模式-可复用面向对象软件的基础》




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Peter(阿斯拉达)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值