C++设计模式——组合模式

问题描述


上图,是一个公司的组织结构图,总部下面有多个子公司,同时总部也有各个部门,子公司下面有多个部门。如果对这样的公司开发一个OA系统,作为程序员的你,如何设计这个OA系统呢?先不说如何设计实现,接着往下看,看完了下面的内容,再回过头来想怎么设计这样的OA系统。

什么是组合模式?

在GOF的《设计模式:可复用面向对象软件的基础》一书中对组合模式是这样说的:将对象组合成树形结构以表示“部分-整体”的层次结构。组合(Composite)模式使得用户对单个对象和组合对象的使用具有一致性。

组合模式(Composite)将小对象组合成树形结构,使用户操作组合对象如同操作一个单个对象。组合模式定义了“部分-整体”的层次结构,基本对象可以被组合成更大的对象,而且这种操作是可重复的,不断重复下去就可以得到一个非常大的组合对象,但这些组合对象与基本对象拥有相同的接口,因而组合是透明的,用法完全一致。

我们这样来简单的理解组合模式,组合模式就是把一些现有的对象或者元素,经过组合后组成新的对象,新的对象提供内部方法,可以让我们很方便的完成这些元素或者内部对象的访问和操作。我们也可以把组合对象理解成一个容器,容器提供各种访问其内部对象或者元素的API,我们只需要使用这些方法就可以操作它了。

UML类图


Component:

  1. 为组合中的对象声明接口;
  2. 在适当的情况下,实现所有类共有接口的缺省行为;
  3. 声明一个接口用于访问和管理Component的子组件。

Leaf:

  1. 在组合中表示叶节点对象,叶节点没有子节点;
  2. 在组合中定义叶节点的行为。

Composite:

  1. 定义有子部件的那些部件的行为;
  2. 存储子部件。

Client:

通过Component接口操作组合部件的对象。

代码实现

   
   
  1. /*
  2. ** FileName : CompositePatternDemo
  3. ** Author : Jelly Young
  4. ** Date : 2013/12/09
  5. ** Description : More information, please go to http://www.jellythink.com
  6. */
  7. #include <iostream>
  8. #include <string>
  9. #include <vector>
  10. using namespace std ;
  11. // 抽象的部件类描述将来所有部件共有的行为
  12. class Component
  13. {
  14. public :
  15. Component ( string name ) : m_strCompname ( name ){}
  16. virtual ~ Component (){}
  17. virtual void Operation () = 0 ;
  18. virtual void Add ( Component *) = 0 ;
  19. virtual void Remove ( Component *) = 0 ;
  20. virtual Component * GetChild ( int ) = 0 ;
  21. virtual string GetName ()
  22. {
  23. return m_strCompname ;
  24. }
  25. virtual void Print () = 0 ;
  26. protected :
  27. string m_strCompname ;
  28. };
  29. class Leaf : public Component
  30. {
  31. public :
  32. Leaf ( string name ) : Component ( name )
  33. {}
  34. void Operation ()
  35. {
  36. cout << ”I’m “ << m_strCompname << endl ;
  37. }
  38. void Add ( Component * pComponent ){}
  39. void Remove ( Component * pComponent ){}
  40. Component * GetChild ( int index )
  41. {
  42. return NULL ;
  43. }
  44. void Print (){}
  45. };
  46. class Composite : public Component
  47. {
  48. public :
  49. Composite ( string name ) : Component ( name )
  50. {}
  51. ~ Composite ()
  52. {
  53. vector < Component *>:: iterator it = m_vecComp . begin ();
  54. while ( it != m_vecComp . end ())
  55. {
  56. if (* it != NULL )
  57. {
  58. cout << ”—-delete “ <<(* it )-> GetName ()<< ”—-“ << endl ;
  59. delete * it ;
  60. * it = NULL ;
  61. }
  62. m_vecComp . erase ( it );
  63. it = m_vecComp . begin ();
  64. }
  65. }
  66. void Operation ()
  67. {
  68. cout << ”I’m “ << m_strCompname << endl ;
  69. }
  70. void Add ( Component * pComponent )
  71. {
  72. m_vecComp . push_back ( pComponent );
  73. }
  74. void Remove ( Component * pComponent )
  75. {
  76. for ( vector < Component *>:: iterator it = m_vecComp . begin (); it != m_vecComp . end (); ++ it )
  77. {
  78. if ((* it )-> GetName () == pComponent -> GetName ())
  79. {
  80. if (* it != NULL )
  81. {
  82. delete * it ;
  83. * it = NULL ;
  84. }
  85. m_vecComp . erase ( it );
  86. break ;
  87. }
  88. }
  89. }
  90. Component * GetChild ( int index )
  91. {
  92. if ( index > m_vecComp . size ())
  93. {
  94. return NULL ;
  95. }
  96. return m_vecComp [ index - 1 ];
  97. }
  98. void Print ()
  99. {
  100. for ( vector < Component *>:: iterator it = m_vecComp . begin (); it != m_vecComp . end (); ++ it )
  101. {
  102. cout <<(* it )-> GetName ()<< endl ;
  103. }
  104. }
  105. private :
  106. vector < Component *> m_vecComp ;
  107. };
  108. int main ( int argc , char * argv [])
  109. {
  110. Component * pNode = new Composite ( ”Beijing Head Office” );
  111. Component * pNodeHr = new Leaf ( ”Beijing Human Resources Department” );
  112. Component * pSubNodeSh = new Composite ( ”Shanghai Branch” );
  113. Component * pSubNodeCd = new Composite ( ”Chengdu Branch” );
  114. Component * pSubNodeBt = new Composite ( ”Baotou Branch” );
  115. pNode -> Add ( pNodeHr );
  116. pNode -> Add ( pSubNodeSh );
  117. pNode -> Add ( pSubNodeCd );
  118. pNode -> Add ( pSubNodeBt );
  119. pNode -> Print ();
  120. Component * pSubNodeShHr = new Leaf ( ”Shanghai Human Resources Department” );
  121. Component * pSubNodeShCg = new Leaf ( ”Shanghai Purchasing Department” );
  122. Component * pSubNodeShXs = new Leaf ( ”Shanghai Sales department” );
  123. Component * pSubNodeShZb = new Leaf ( ”Shanghai Quality supervision Department” );
  124. pSubNodeSh -> Add ( pSubNodeShHr );
  125. pSubNodeSh -> Add ( pSubNodeShCg );
  126. pSubNodeSh -> Add ( pSubNodeShXs );
  127. pSubNodeSh -> Add ( pSubNodeShZb );
  128. pNode -> Print ();
  129. // 公司不景气,需要关闭上海质量监督部门
  130. pSubNodeSh -> Remove ( pSubNodeShZb );
  131. if ( pNode != NULL )
  132. {
  133. delete pNode ;
  134. pNode = NULL ;
  135. }
  136. return 0 ;
  137. }
实现要点
  1. Composite的关键之一在于一个抽象类,它既可以代表Leaf,又可以代表Composite;所以在实际实现时,应该最大化Component接口,Component类应为Leaf和Composite类尽可能多定义一些公共操作。Component类通常为这些操作提供缺省的实现,而Leaf和Composite子类可以对它们进行重定义;
  2. Component是否应该实现一个Component列表,在上面的代码中,我是在Composite中维护的列表,由于在Leaf中,不可能存在子Composite,所以在Composite中维护了一个Component列表,这样就减少了内存的浪费;
  3. 内存的释放;由于存在树形结构,当父节点都被销毁时,所有的子节点也必须被销毁,所以,我是在析构函数中对维护的Component列表进行统一销毁,这样就可以免去客户端频繁销毁子节点的困扰;
  4. 由于在Component接口提供了最大化的接口定义,导致一些操作对于Leaf节点来说并不适用,比如:Leaf节点并不能进行Add和Remove操作,由于Composite模式屏蔽了部分与整体的区别,为了防止客户对Leaf进行非法的Add和Remove操作,所以,在实际开发过程中,进行Add和Remove操作时,需要进行对应的判断,判断当前节点是否为Composite。
组合模式的优点

将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

使用场景
  1. 你想表示对象的部分-整体层次结构;
  2. 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

引用大话设计模式的片段:“当发现需求中是体现部分与整体层次结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑组合模式了。”

总结

通过上面的简单讲解,我们知道了,组合模式意图是通过整体与局部之间的关系,通过树形结构的形式进行组织复杂对象,屏蔽对象内部的细节,对外展现统一的方式来操作对象,是我们处理更复杂对象的一个手段和方式。现在再结合上面的代码,想想文章开头提出的公司OA系统如何进行设计。

原文链接:http://www.jellythink.com/archives/149

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值