第十九章分公司=一部门--组合模式
19.1 分公司不就是一部门吗?
例如卖电脑的商家,可以卖单独配件也可以卖组装整机,又如复制文件,可以一个一个文件复制粘贴还可以整个文件夹进行复制,再比如文本编辑,可以给单个字加粗,变色,改字体,当然也可以给整段文字做同样的操作。分公司或办事处与总公司的关系,也是部分和整体的关系。人力资源部,财务部的管理功能可以复用于分公司。这其实就是整体与部分可以被一致对待的问题。
如果北京总公司当作一棵大树的根部的话,他的下属分公司其实就是这棵树的分枝。而他们的相关的职能部门由于没有分枝了,所以可以理解为树叶。
19.2 组合模式
将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component。
Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作。比如增加Add和删除Remove。
客户端代码,能通过Component接口操作组合部件的对象。
#pragma once
#include <string>
#include <iostream>
#include <list>
class Component
{
public:
Component(std::string name)
{
m_Name = name;
};
~Component(){};
virtual void Add(Component* p) = 0;
virtual void Remove(Component* p) = 0;
virtual void Display(int depth) = 0;
protected:
std::string m_Name;
};
class Leaf :public Component
{
public:
Leaf(std::string name)
:Component(name)
{
};
void Add(Component* p)
{
std::cout << "Can not add a Leaf." << std::endl;
};
void Remove(Component* p)
{
std::cout << "Can not remove from a Leaf." << std::endl;
};
void Display(int depth)
{
std::cout << depth << m_Name << std::endl;
};
};
class Composite : public Component
{
public:
Composite(std::string name)
:Component(name)
{
m_children.clear();
};
~Composite()
{
if ( !m_children.empty() )
{
std::list<Component*>::iterator iter = m_children.begin();
while ( iter != m_children.end() )
{
delete *iter;
++iter;
}
}
m_children.clear();
};
void Add(Component* p)
{
m_children.push_back(p);
};
void Remove(Component* p)
{
m_children.remove(p);
};
void Display(int depth)
{
std::cout << depth << m_Name << std::endl;
if ( !m_children.empty() )
{
std::list<Component*>::iterator iter = m_children.begin();
while ( iter != m_children.end() )
{
(*iter)->Display(depth + 2);
++iter;
}
}
};
private:
std::list<Component*> m_children;
};
#include "stdafx.h"
#include "Component.h"
int _tmain(int argc, _TCHAR* argv[])
{
Composite* root = new Composite("root");
root->Add(new Leaf("Leaf A"));
root->Add(new Leaf("Leaf B"));
Composite* comp = new Composite("Composite A");
comp->Add(new Leaf("Leaf XA"));
comp->Add(new Leaf("Leaf XB"));
Composite* comp2 = new Composite("Composite XY");
comp2->Add(new Leaf("Leaf XYA"));
comp2->Add(new Leaf("Leaf XYB"));
comp->Add(comp2);
root->Add(comp);
root->Add(new Leaf("Leaf C"));
Leaf* leaf = new Leaf("Leaf D");
root->Add(leaf);
root->Remove(leaf);
root->Display(1);
delete root;
return 0;
}
树可能有无数的分枝,但只需要反复用Composite就可以实现树状结构了。
19.3 透明方式与安全方式
透明方式:在Component中声明所以后用来管理子对象的方法,其中包括Add,Remove等。这样实现Component接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对于外界没有区别,他们具备完全一致的行为接口。但问题也很明显,因为Leaf类本身不具备Add(),Remove()方法的功能,所以实现它是没有意义的。
安全模式:在Component中不去声明Add(),Remove()方法,这样做就不会出现刚才提到的问题,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。
两者各有好处,视情况而定用哪种方式。
19.4 何时使用组合模式
需求中是体现部分与整体层次结构时,希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式。ASP.NET的TreeView控件就是典型的组合模式应用。
19.5 公司管理系统
公司类:抽象类或者接口,在这里定义了履行职责的方法,不同的部门需要履行不同的职责。
具体公司类:实现接口,树枝节点
#pragma once
#include <string>
#include <list>
#include <iostream>
class Company
{
public:
Company(std::string name)
{
m_Name = name;
};
virtual ~Company(){};
virtual void Add(Company* p) = 0;
virtual void Remove(Company* p) = 0;
virtual void Display(int depth) = 0;
//怑愑傪壥偨偡
virtual void LineOfDuty(void) = 0;
protected:
std::string m_Name;
};
class ConcreteCompany : public Company
{
public:
ConcreteCompany(std::string name)
:Company(name)
{
m_chirdren.clear();
};
~ConcreteCompany()
{
if (!m_chirdren.empty())
{
m_chirdrenIter = m_chirdren.begin();
while (m_chirdrenIter != m_chirdren.end())
{
delete *m_chirdrenIter;
++m_chirdrenIter;
}
}
m_chirdren.clear();
};
void Add(Company* p)
{
m_chirdren.push_back(p);
};
void Remove(Company* p)
{
m_chirdren.remove(p);
};
void Display(int depth)
{
std::cout << depth << m_Name << std::endl;
if (!m_chirdren.empty())
{
m_chirdrenIter = m_chirdren.begin();
while (m_chirdrenIter != m_chirdren.end())
{
(*m_chirdrenIter)->Display(depth + 2);
++m_chirdrenIter;
}
}
};
//怑愑傪壥偨偡
void LineOfDuty(void)
{
if (!m_chirdren.empty())
{
m_chirdrenIter = m_chirdren.begin();
while (m_chirdrenIter != m_chirdren.end())
{
(*m_chirdrenIter)->LineOfDuty();
++m_chirdrenIter;
}
}
};
private:
std::list<Company*> m_chirdren;
std::list<Company*>::iterator m_chirdrenIter;
};
class HRDepartment :public Company
{
public:
HRDepartment(std::string name)
:Company(name)
{
};
void Add(Company* p)
{
};
void Remove(Company* p)
{
};
void Display(int depth)
{
std::cout << depth << m_Name << std::endl;
};
void LineOfDuty(void)
{
std::cout << "HR偺巇帠傪傗傝傑偡." << m_Name << std::endl;
};
};
class FinanceDepartment :public Company
{
public:
FinanceDepartment(std::string name)
:Company(name)
{
};
void Add(Company* p)
{
};
void Remove(Company* p)
{
};
void Display(int depth)
{
std::cout << depth << m_Name << std::endl;
};
void LineOfDuty(void)
{
std::cout << "夛幮偺嵿柋傪娗棟偟傑偡." << m_Name << std::endl;
};
};
客户端调用
#include "stdafx.h"
#include "Company.h"
int _tmain(int argc, _TCHAR* argv[])
{
ConcreteCompany* root = new ConcreteCompany("杒嫗憤夛幮");
root->Add(new HRDepartment("憤夛幮恖帠帒尮晹"));
root->Add(new FinanceDepartment("憤夛幮嵿柋晹"));
ConcreteCompany* comp = new ConcreteCompany("忋奀暘夛幮");
comp->Add(new HRDepartment("忋奀暘夛幮恖帠帒尮晹"));
comp->Add(new FinanceDepartment("忋奀暘夛幮嵿柋晹"));
root->Add(comp);
ConcreteCompany* comp2 = new ConcreteCompany("撿嫗暘夛幮");
comp2->Add(new HRDepartment("撿嫗暘夛幮恖帠帒尮晹"));
comp2->Add(new FinanceDepartment("撿嫗暘夛幮嵿柋晹"));
comp->Add(comp2);
ConcreteCompany* comp3 = new ConcreteCompany("峐廈暘夛幮");
comp3->Add(new HRDepartment("峐廈暘夛幮恖帠帒尮晹"));
comp3->Add(new FinanceDepartment("峐廈暘夛幮嵿柋晹"));
comp->Add(comp3);
std::cout << "峔憿恾" << std::endl;
root->Display(1);
std::cout << "怑愑" << std::endl;
root->LineOfDuty();
delete root;
return 0;
}
19.6 组合模式好处
组合模式这样就定义了包含人力资源部和财务部这些基本对象和分公司,办事处等组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
用户不用关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。组合结构让客户可以一致地使用组合结构和单个对象。