问题描述
上图,是一个公司的组织结构图,总部下面有多个子公司,同时总部也有各个部门,子公司下面有多个部门。如果对这样的公司开发一个OA系统,作为程序员的你,如何设计这个OA系统呢?先不说如何设计实现,接着往下看,看完了下面的内容,再回过头来想怎么设计这样的OA系统。
什么是组合模式?
在GOF的《设计模式:可复用面向对象软件的基础》一书中对组合模式是这样说的:将对象组合成树形结构以表示“部分-整体”的层次结构。组合(Composite)模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式(Composite)将小对象组合成树形结构,使用户操作组合对象如同操作一个单个对象。组合模式定义了“部分-整体”的层次结构,基本对象可以被组合成更大的对象,而且这种操作是可重复的,不断重复下去就可以得到一个非常大的组合对象,但这些组合对象与基本对象拥有相同的接口,因而组合是透明的,用法完全一致。
我们这样来简单的理解组合模式,组合模式就是把一些现有的对象或者元素,经过组合后组成新的对象,新的对象提供内部方法,可以让我们很方便的完成这些元素或者内部对象的访问和操作。我们也可以把组合对象理解成一个容器,容器提供各种访问其内部对象或者元素的API,我们只需要使用这些方法就可以操作它了。
UML类图
Component:
- 为组合中的对象声明接口;
- 在适当的情况下,实现所有类共有接口的缺省行为;
- 声明一个接口用于访问和管理Component的子组件。
Leaf:
- 在组合中表示叶节点对象,叶节点没有子节点;
- 在组合中定义叶节点的行为。
Composite:
- 定义有子部件的那些部件的行为;
- 存储子部件。
Client:
通过Component接口操作组合部件的对象。
代码实现
-
/*
-
** FileName : CompositePatternDemo
-
** Author : Jelly Young
-
** Date : 2013/12/09
-
** Description : More information, please go to http://www.jellythink.com
-
*/
-
#include
<iostream>
-
#include
<string>
-
#include
<vector>
-
using
namespace
std
;
-
// 抽象的部件类描述将来所有部件共有的行为
-
class
Component
-
{
-
public
:
-
Component
(
string
name
)
:
m_strCompname
(
name
){}
-
virtual
~
Component
(){}
-
virtual
void
Operation
()
=
0
;
-
virtual
void
Add
(
Component
*)
=
0
;
-
virtual
void
Remove
(
Component
*)
=
0
;
-
virtual
Component
*
GetChild
(
int
)
=
0
;
-
virtual
string
GetName
()
-
{
-
return
m_strCompname
;
-
}
-
virtual
void
Print
()
=
0
;
-
protected
:
-
string
m_strCompname
;
-
};
-
class
Leaf
:
public
Component
-
{
-
public
:
-
Leaf
(
string
name
)
:
Component
(
name
)
-
{}
-
void
Operation
()
-
{
-
cout
<<
”I’m “
<<
m_strCompname
<<
endl
;
-
}
-
void
Add
(
Component
*
pComponent
){}
-
void
Remove
(
Component
*
pComponent
){}
-
Component
*
GetChild
(
int
index
)
-
{
-
return
NULL
;
-
}
-
void
Print
(){}
-
};
-
class
Composite
:
public
Component
-
{
-
public
:
-
Composite
(
string
name
)
:
Component
(
name
)
-
{}
-
~
Composite
()
-
{
-
vector
<
Component
*>::
iterator it
=
m_vecComp
.
begin
();
-
while
(
it
!=
m_vecComp
.
end
())
-
{
-
if
(*
it
!=
NULL
)
-
{
-
cout
<<
”—-delete “
<<(*
it
)->
GetName
()<<
”—-“
<<
endl
;
-
delete
*
it
;
-
*
it
=
NULL
;
-
}
-
m_vecComp
.
erase
(
it
);
-
it
=
m_vecComp
.
begin
();
-
}
-
}
-
void
Operation
()
-
{
-
cout
<<
”I’m “
<<
m_strCompname
<<
endl
;
-
}
-
void
Add
(
Component
*
pComponent
)
-
{
-
m_vecComp
.
push_back
(
pComponent
);
-
}
-
void
Remove
(
Component
*
pComponent
)
-
{
-
for
(
vector
<
Component
*>::
iterator it
=
m_vecComp
.
begin
();
it
!=
m_vecComp
.
end
();
++
it
)
-
{
-
if
((*
it
)->
GetName
()
==
pComponent
->
GetName
())
-
{
-
if
(*
it
!=
NULL
)
-
{
-
delete
*
it
;
-
*
it
=
NULL
;
-
}
-
m_vecComp
.
erase
(
it
);
-
break
;
-
}
-
}
-
}
-
Component
*
GetChild
(
int
index
)
-
{
-
if
(
index
>
m_vecComp
.
size
())
-
{
-
return
NULL
;
-
}
-
return
m_vecComp
[
index
-
1
];
-
}
-
void
Print
()
-
{
-
for
(
vector
<
Component
*>::
iterator it
=
m_vecComp
.
begin
();
it
!=
m_vecComp
.
end
();
++
it
)
-
{
-
cout
<<(*
it
)->
GetName
()<<
endl
;
-
}
-
}
-
private
:
-
vector
<
Component
*>
m_vecComp
;
-
};
-
int
main
(
int
argc
,
char
*
argv
[])
-
{
-
Component
*
pNode
=
new
Composite
(
”Beijing Head Office”
);
-
Component
*
pNodeHr
=
new
Leaf
(
”Beijing Human Resources Department”
);
-
Component
*
pSubNodeSh
=
new
Composite
(
”Shanghai Branch”
);
-
Component
*
pSubNodeCd
=
new
Composite
(
”Chengdu Branch”
);
-
Component
*
pSubNodeBt
=
new
Composite
(
”Baotou Branch”
);
-
pNode
->
Add
(
pNodeHr
);
-
pNode
->
Add
(
pSubNodeSh
);
-
pNode
->
Add
(
pSubNodeCd
);
-
pNode
->
Add
(
pSubNodeBt
);
-
pNode
->
Print
();
-
Component
*
pSubNodeShHr
=
new
Leaf
(
”Shanghai Human Resources Department”
);
-
Component
*
pSubNodeShCg
=
new
Leaf
(
”Shanghai Purchasing Department”
);
-
Component
*
pSubNodeShXs
=
new
Leaf
(
”Shanghai Sales department”
);
-
Component
*
pSubNodeShZb
=
new
Leaf
(
”Shanghai Quality supervision Department”
);
-
pSubNodeSh
->
Add
(
pSubNodeShHr
);
-
pSubNodeSh
->
Add
(
pSubNodeShCg
);
-
pSubNodeSh
->
Add
(
pSubNodeShXs
);
-
pSubNodeSh
->
Add
(
pSubNodeShZb
);
-
pNode
->
Print
();
-
// 公司不景气,需要关闭上海质量监督部门
-
pSubNodeSh
->
Remove
(
pSubNodeShZb
);
-
if
(
pNode
!=
NULL
)
-
{
-
delete
pNode
;
-
pNode
=
NULL
;
-
}
-
return
0
;
-
}
实现要点
- Composite的关键之一在于一个抽象类,它既可以代表Leaf,又可以代表Composite;所以在实际实现时,应该最大化Component接口,Component类应为Leaf和Composite类尽可能多定义一些公共操作。Component类通常为这些操作提供缺省的实现,而Leaf和Composite子类可以对它们进行重定义;
- Component是否应该实现一个Component列表,在上面的代码中,我是在Composite中维护的列表,由于在Leaf中,不可能存在子Composite,所以在Composite中维护了一个Component列表,这样就减少了内存的浪费;
- 内存的释放;由于存在树形结构,当父节点都被销毁时,所有的子节点也必须被销毁,所以,我是在析构函数中对维护的Component列表进行统一销毁,这样就可以免去客户端频繁销毁子节点的困扰;
- 由于在Component接口提供了最大化的接口定义,导致一些操作对于Leaf节点来说并不适用,比如:Leaf节点并不能进行Add和Remove操作,由于Composite模式屏蔽了部分与整体的区别,为了防止客户对Leaf进行非法的Add和Remove操作,所以,在实际开发过程中,进行Add和Remove操作时,需要进行对应的判断,判断当前节点是否为Composite。
组合模式的优点
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
使用场景
- 你想表示对象的部分-整体层次结构;
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
引用大话设计模式的片段:“当发现需求中是体现部分与整体层次结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑组合模式了。”
总结
通过上面的简单讲解,我们知道了,组合模式意图是通过整体与局部之间的关系,通过树形结构的形式进行组织复杂对象,屏蔽对象内部的细节,对外展现统一的方式来操作对象,是我们处理更复杂对象的一个手段和方式。现在再结合上面的代码,想想文章开头提出的公司OA系统如何进行设计。