设计模式(九):Composite组合模式 -- 结构型模式
1. 概述
在数据结构里面,树结构是很重要,我们可以把树的结构应用到设计模式里面。
例子1:就是多级树形菜单。
例子2:文件和文件夹目录
2.问题
我们可以使用简单的对象组合成复杂的对象,而这个复杂对象有可以组合成更大的对象。我们可以把简单这些对象定义成类,然后定义一些容器类来存储这些简单对象。客户端代码必须区别对象简单对象和容器对象,而实际上大多数情况下用户认为它们是一样的。对这些类区别使用,使得程序更加复杂。递归使用的时候跟麻烦,而我们如何使用递归组合,使得用户不必对这些类进行区别呢?
3. 解决方案
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
组合模式让你可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。关于分级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你可以使用组合模式Composite。
4. 组合模式的分类
1) 将管理子元素的方法定义在Composite类中
2) 将管理子元素的方法定义在Component接口中,这样Leaf类就需要对这些方法空实现。
5. 适用性
以下情况下适用Composite模式:
1).你想表示对象的部分-整体层次结构
2).你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
6.
结构
典型的C
o m p o s i t e对象结构如下图所示:
7. 构建模式的组成
抽象构件角色(component):是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
这个接口可 以用来管理所有的子对象。(可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。
树叶构件角色(Leaf):在组合树中表示叶节点对象,叶节点没有子节点。并在组合中定义图元对象的行为。
树枝构件角色(Composite):定义有子部件的那些部件的行为。存储子部件。在Component接口中实现与子部件有关的操作。
客户角色(Client):通过component接口操纵组合部件的对象。
8. 效果
1) • 定义了包含基本对象和组合对象的类层次结构 基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去。客户代码中,任何用到 基本对象的地方都可以使用组合对象。
2) • 简化客户代码 客户可以一致地使用组合结构和单个对象。通常用户不知道 (也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码 , 因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
3) • 使得更容易增加新类型的组件 新定义的Composite或Leaf子类自动地与已有的结构和客户代码一起工作,客户程序不需因新的Component类而改变。
4) • 使你的设计变得更加一般化 容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时你希望一个组合只能有某些特定的组件。使用Composite时,你不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。
9. 实现
比较经典的例子是树形菜单。多级展示,这个菜单可以无限增加节点;例外就是文件遍历等等。
- <?php
- /**
- * 组合模式
- *
- * @author guisu
- * @version 1.0
- * 组合模式:树形菜单
- *
- * 将对象组合成树形结构以表示"部分-整体"的层次结构,使得客户对单个对象和复合对象的使用具有一致性
- */
- /**
- * 抽象构件角色(component)
- *
- */
- abstract class MenuComponent
- {
- public function add($component){}
- public function remove($component){}
- public function getName(){}
- public function getUrl(){}
- public function displayOperation(){}
- }
- /**
- * 树枝构件角色(Composite)
- *
- */
- class MenuComposite extends MenuComponent
- {
- private $_items = array();
- private $_name = null;
- private $_align = '';
- public function __construct($name) {
- $this->_name = $name;
- }
- public function add($component) {
- $this->_items[$component->getName()] = $component;
- }
- public function remove($component) {
- $key = array_search($component,$this->_items);
- if($key !== false) unset($this->_items[$key]);
- }
- public function getItems() {
- return $this->_items;
- }
- public function displayOperation() {
- static $align = '|';
- if($this->getItems()) {
- //substr($align, strpos($align,));
- $align .= ' _ _ ';
- }else{
- $align .='';
- }
- echo $this->_name, " <br/>";
- foreach($this->_items as $name=> $item) {
- echo $align;
- $item->displayOperation();
- }
- }
- public function getName(){
- return $this->_name;
- }
- }
- /**
- *树叶构件角色(Leaf)
- *
- */
- class ItemLeaf extends MenuComponent
- {
- private $_name = null;
- private $_url = null;
- //public $_align = '----';
- public function __construct($name,$url)
- {
- $this->_name = $name;
- $this->_url = $url;
- }
- public function displayOperation()
- {
- echo '<a href="', $this->_url, '">' , $this->_name, '</a><br/>';
- }
- public function getName(){
- return $this->_name;
- }
- }
- class Client
- {
- public static function displayMenu()
- {
- $subMenu1 = new MenuComposite("submenu1");
- $subMenu2 = new MenuComposite("submenu2");
- $subMenu3 = new MenuComposite("submenu3");
- $subMenu4 = new MenuComposite("submenu4");
- $subMenu5 = new MenuComposite("submenu5");
- /*
- $item1 = new ItemLeaf("sohu","www.163.com");
- $item2 = new ItemLeaf("sina","www.sina.com");
- $subMenu4 = new MenuComposite("submenu4");
- $subMenu1->add($subMenu4);
- $subMenu4->add($item1);
- $subMenu4->add($item2);
- */
- $item3 = new ItemLeaf("baidu","www.baidu.com");
- $item4 = new ItemLeaf("google","www.google.com");
- $subMenu2->add($item3);
- $subMenu2->add($item4);
- $allMenu = new MenuComposite("AllMenu");
- $allMenu->add($subMenu1);
- $allMenu->add($subMenu2);
- $allMenu->add($subMenu3);
- $subMenu3->add($subMenu4);
- $subMenu4->add($subMenu5);
- $allMenu->displayOperation();
- }
- }
- // 创建menu
- Client::displayMenu();
- ?>
10. 组合模式和其他相关模式
1)装饰模式(Decorator模式)经常与Composite模式一起使用。当装饰和组合一起使用时,它们
通常有一个公共的父类。因此装饰必须支持具有 Add、Remove和GetChild 操作的Component接口。
2)Flyweight模式让你共享组件,但不再能引用他们的父部件。
3)(迭代器模式)Itertor可用来遍历Composite。
4)(观察者模式)Visitor将本来应该分布在Composite和L e a f类中的操作和行为局部化。
11. 总结
组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以向处理简单元素一样来处理复杂元素。
如果你想要创建层次结构,并可以在其中以相同的方式对待所有元素,那么组合模式就是最理想的选择。
设计模式(九):Composite组合模式 -- 结构型模式的更多相关文章
- .NET设计模式(15):结构型模式专题总结(转)
摘要:结构型模式,顾名思义讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象,从而实现新的功能(对象结构型模式).这些结构型模式,它们在某些方面具有很大的相似 ...
- 设计模式(十二): Flyweight享元模式 -- 结构型模式
说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面 ...
- 代理模式/proxy模式/结构型模式
代理模式proxy 定义 为其他对象提供一种代理,并以控制对这个对象的访问.最简单的理解,买东西都是要去商店的,不会去工厂. java实现三要素 proxy(代理)+subject(接口)+realS ...
- Composite组合模式(结构型模式)
1.概述 在面向对象系统中,经常会遇到一些具有"容器性质"的对象,它们自己在充当容器的同时,也充当其他对象的容器. 2.案例 需要构建一个容器系统,需要满足以下几点要求: (1). ...
- 设计模式(十):Decorator装饰者模式 -- 结构型模式
1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继 ...
- 设计模式(十三): Proxy代理模式 -- 结构型模式
设计模式(十一)代理模式Proxy(结构型) 1.概述 因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路 ...
- 设计模式学习之路——Facade 外观模式(结构型模式)
动机: 组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战.如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系 ...
- 设计模式(十一):FACADE外观模式 -- 结构型模式
1. 概述 外观模式,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性.例子1:一个电源总开关可以控制四盏灯.一个风扇 ...
- 设计模式(八):Bridge桥接模式 -- 结构型模式
1. 概述 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度 ...
随机推荐
- where子句的具体含义
今天同学让我帮他调代码,下面是出错的那句: txtSQL= "select * from student_Info where UserID='" & cboUserID. ...
- Robotium -- 针对apk包的测试
在使用Robotium测试的时候,有时候,测试人员并没有代码权限,而Robotium也可以在只有apk文件进行测试,下面就介绍一下这个过程. 1.设置环境变量 安装jdk环境和sdk环境 2.安装签名 ...
- huffman编码——原理与实现
哈夫曼算法原理 Wikipedia上面说的非常清楚了,这里我就不再赘述,直接贴过来了. 1952年, David A. Huffman提出了一个不同的算法,这个算法能够为不论什么的可能性提供出一个理想 ...
- Java Performance Optimization Tools and Techniques for Turbocharged Apps--reference
Java Performance Optimization by: Pierre-Hugues Charbonneau reference:http://refcardz.dzone.com/refc ...
- android studio 的部分设置
1.android studio 如何提示方法的用法 在 Eclipse中鼠标放上去就可以提示方法的用法,实际上Android Studio也可以设置的.如图 Preferences > Edi ...
- memcache和memcached
一:Memcached.memcached.memcache. 其中首字母大写的Memcached,指的是Memcached服务器,就是独立运行Memcached的后台服务器,用于存储数据的“数据库” ...
- Carthage&&cocopads 摘抄笔记
Carthage 是 iOS/Mac 开发生态圈的一个包管理工具,与现在流行的 CocoaPods 不同,它是一个去中心化的解决方案.知道它已经有一段时间了,但是一直没有好好玩过,今天整合 Carth ...
- 将图片转换为Base64
string Imagefilename 硬盘路径 protected string ImgToBase64String(string Imagefilename) { try { Bitmap ...
- PHP 类型比较表
以下的表格显示了 PHP 类型和比较运算符在松散和严格比较时的作用.该补充材料还和类型戏法的相关章节内容有关.同时,大量的用户注释和 » BlueShoes 的工作也给该材料提供了帮助. 在使用这些表 ...
- PHP中的定界符格式
<?php //nowdoc(单引号定界符) //ABC可以是任合内容,放在单引号中 $c=<<<'ABC' 这里可以是任合内容 我是历的苛夺基 本原则叶落归根在运 输费艰难田 ...