当前位置: 首页 > 编程日记 > 正文

设计模式笔记(9)---组合模式(结构型)

Gof定义

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

在面向对象系统中,我们经常会遇到一类具有”容器“特征的对象---即他们在充当对象的同时,又是其他对象的容器。比如在一些管理系统中就会出现总公司下面有很多的分公司,分公司下面有很多的部门,每个部门下又有很多的员工,像分公司和部门就是既充当了“对象”的角色也充当了“容器”的角色;还有我们可能玩过的“俄罗斯套娃”也是这种结构,一个大娃娃里面装个小娃娃,小的里面又有个小的直到最小的一个,中间的娃娃就是既充当了“对象”也充当了“容器”。先看下面这个例子:

interface IBox
{void Process();
}
/// <summary>
/// 相当于树的叶子节点,没有子对象了
/// </summary>
public class SingleBox : IBox
{public void Process(){ }
}
/// <summary>
/// 容器
/// </summary>
public class ContainerBox : IBox
{List<IBox> list = new List<IBox>();public void Process() { }public List<IBox> GetBoxes(){return list;}
}
/// <summary>
/// 客户代码
/// </summary>
public class App
{static void Mina(){///此处从工厂方法中得到盒子的对象,但我们不知道是SingleBox还是ContainerBox///所以要做判断IBox box = Factory.Create();if (box is ContainerBox){box.Process();List<IBox> list = ((ContainerBox)box).GetBoxes();}else if(box is SingleBox){box.Process();}}
}

动机

上面代码的问题的根据在于:客户代码过多地依赖对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。

如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

这就要使用Composite模式了,先看下Composite模式的结构图

2009-11-24_073228

Component:定义了Leaf和Composite的一些共有特性。

Composite:有容器特征的类型。

Leaf:叶节点,即一个单独的个体,下面没有子节点。

依据上面的结构图完成代码实现:

public abstract class Component
{protected string _name;public Component(string name){_name = name;}public abstract void Operation();public abstract void Add(Component component);public abstract void Remove(Component component);
}public class Leaf : Component
{public Leaf(string name) : base(name) { }public override void Add(Component component){throw new NotSupportedException();}public override void Remove(Component component){throw new NotSupportedException();}public override void Operation(){ //...do something}
}public class Composite : Component
{public Composite(string name):base(name){}List<Component> list = new List<Component>();/// <summary>/// 添加/// </summary>/// <param name="conponent"></param>public override void Add(Component component){if (list != null){list = new List<Component>();}list.Add(component);}/// <summary>/// 删除/// </summary>/// <param name="component"></param>public override void Remove(Component component){if (list == null){throw new NullReferenceException();}list.Remove(component);}public override void Operation(){if (list != null){foreach (Component c in list){c.Operation();}}}
}

Leaf类为叶子节点类,它的实例是没有子节点的,但是在抽象类中的方法Add和Remove方法必须要实现,按理说这样的实现是没有意义的,所以在此处抛出了NotSupportedException 异常,在客户端调用捕获到再做相应的处理,这种模式称之为“透明足组合模式”,这样做的好处是叶子(Leaf)和容器(Composite)对于外界没有分别,它们具有一致的接口行为。还有一种情况叫“安全组合模式”,在抽象类(Component)中不定义Add Remove方法,而是在容器的实现类中去定义,这样就各司其职了,看下面结构图

2009-11-24_232154

代码实现

public abstract class Component
{protected string _name;public Component(string name){_name = name;}public abstract void Operation();
}public class Leaf : Component
{public Leaf(string name) : base(name) { }public override void Operation(){//...do something}
}public class Composite : Component
{public Composite(string name) : base(name) { }List<Component> list = new List<Component>();/// <summary>/// 添加/// </summary>/// <param name="conponent"></param>public void Add(Component component){if (list != null){list = new List<Component>();}list.Add(component);}/// <summary>/// 删除/// </summary>/// <param name="component"></param>public void Remove(Component component){if (list == null){throw new NullReferenceException();}list.Remove(component);}public override void Operation(){if (list != null){foreach (Component c in list){c.Operation();}}}
}

使用“安全组合模式”Leaf和Composite就不具有相同的接口,所以在客户端调用的时候还是要去判断是什么类型,比较麻烦。所以说选择哪种还要看具体的需求。

Composite模式的几个要点

Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象还是组合的对象容器。

将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思>想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能“应对变化”。

Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.NET控件的实现在这方面为我们提供了一个很好的示范。

Composite模式在具体实现中,可以让父对象中的子对象反向追溯; 如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

转载于:https://www.cnblogs.com/oec2003/archive/2009/12/02/1615773.html

相关文章:

npm install出现的错误

在使用cnpm 安装node依赖包的时候&#xff0c;出现上述错误&#xff0c;网上搜索&#xff0c;说是有两个解决方案&#xff1a; 虽然提示不适合Windows&#xff0c;但是问题好像是sass loader出问题的。所以只要执行下面命令即可&#xff1b; 方案一&#xff1a; cnpm rebuild n…

射频放大器芯片3阶截点计算与芯片选择

在选用射频放大器芯片时&#xff0c;除了需要考虑增益、噪声指数等指标外&#xff0c;线性指标也是必须关注的参数。在射频或微波多载波通讯系统中&#xff0c;三阶交调截取点IP3&#xff08;Third-order Intercept Point&#xff09;是一个衡量线性度或失真的重要指标。交调失…

Python-接口自动化(二)

python基础知识&#xff08;二&#xff09; &#xff08;二&#xff09;常用控制流 1、控制语句 分支语句&#xff1a;起到一个分支分流的作用&#xff0c;类似马路上的红绿灯 循环语句&#xff1a;for while 可以使代码不断重复的执行 2、判断语句&#xff1a;关键字是if..eli…

Angular CLI在线安装和离线安装

Angular CLI 安装方式 默认已经安装了 Node.js 和 npm 包管理器。 1. 在线安装 可以使用外网的情况下&#xff0c;可以使用在线安装的方式。 要使用 npm 命令全局安装 CLI&#xff0c;请打开终端/控制台窗口&#xff0c;输入如下命令&#xff1a; npm install -g angular/…

近来工作和面试一些人的感受(原)

最近公司招聘&#xff0c;面试了很多人&#xff0c;有牛人 - 无所不能的&#xff0c;自认为没必要再提高的牛人&#xff0c;有硕士&#xff0c;有啥都不懂乱投简历的&#xff0c;有简历项目经验写几十个的各种技术都精通的&#xff0c;还有水平一般却要求薪水很高的&#xff0c…

关于vue+webpack的一点配置

开发环境跨域访问&#xff1a; config/index.js 增加proxyTable里的内容&#xff0c;然后可以在config/dev.env.js中设置访问地址的origin为"/api" 本地图片访问问题&#xff1a; 一般&#xff0c;放在static下&#xff0c;图片访问地址设置成‘./static/....’ js等…

2.5Gb/s混合集成光发射机

0、引言<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />在信息呈爆炸式发展的今天&#xff0c;光纤通信已成为现代信息网络的主要传输手段&#xff0c;近几年国家干线网大部分仍采用2.5Gb/s系统, 10Gb/s系统正积极研制开发&…

laravel5.8的使用

首先&#xff0c;确定电脑已经安装了composer。最好是全局安装 然后打开phpstorm的控制台&#xff1a; composer create-project --prefer-dist laravel/laravel blog另外一种方式步骤多。然后中间配置的地方又多&#xff0c;不推荐。 artisan 在Laravel根目录下运行&#xff1…

npm install 报错 npm ERR! code Z_BUF_ERROR 问题解决

问题描述&#xff1a; 使用npm install命令安装依赖时&#xff0c;出现错误&#xff0c;报错信息如下&#xff1a; npm ERR! code Z_BUF_ERROR npm ERR! errno -5 npm ERR! zlib: unexpected end of file解决方式&#xff1a; 使用如下命令安装淘宝镜像后&#xff0c;重新执…

BZOJ——1202: [HNOI2005]狡猾的商人

http://www.lydsy.com/JudgeOnline/problem.php?id1202 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4075 Solved: 1958[Submit][Status][Discuss] Description 刁姹接到一个任务&#xff0c;为税务部门调查一位商人的账本&#xff0c;看看账本是不是伪造的。账本上记录…

ASP.NET禁用视图状态

1、站点禁用视图状态<configuration> <system.web> <pages enableViewState"false"/> </system.web> </configuration>2、页面禁用视图状态<% Page EnableViewState"false" %>转载于:https://www.cnb…

Virtual PC磁盘的最佳压缩方式

随着vpc不断的使用,vpc的磁盘就会一天一天的增大,于是你试着去把那些在vpc上面的软件都删除了,可是发现体积仍然没有什么改观,我还尝试过将系统都格式化了,仍然没有什么太大的变化. 经过苦苦搜寻还是得到了大数人提供的解决办法:首先启动虚拟机进到系统,然后装载母机vpc安装目录…

OO第一次总结

一&#xff0e;基于度量的程序结构分析 在进行分析之前&#xff0c;先解释一下以下几个缩写&#xff1a; LOC&#xff1a;代码行数 CC&#xff1a;圈复杂度&#xff0c;反映了程序中if/while等判定条件的数量&#xff0c;越高意味着代码越可能质量低且难以测试、维护。 PC&…

python学习笔记(一)之入门

1、python的安装 官网下载.exe文件直接安装即可&#xff0c;在安装过程中选择加入环境变量&#xff0c;就不用在安装后再去增添环境变量了。 本文选择的是python3.6版本&#xff0c;没有选择2.7版本。 2、启动python解释器和运行.py文件 安装过程中选择创建桌面图标连接&#x…

丽水风光(二)—劫色“古堰画乡”

丽水风光 &#xff08;二&#xff09; 劫色古堰画乡 驱车从鸥江到古堰画乡大约二十分钟。一路由同学&#xff2c;老弟相陪&#xff0c;车刚停在江边&#xff0c;我就被美景陶醉&#xff0c;撇下老同学&#xff0c;旁若无人地与&#xff38;兄一边卡嚓卡嚓去了&#xff0c;一副“…

爱情神话:庄妃用美色套牢洪承畴之谜

题记&#xff1a;庄妃&#xff0c;一个蒙古族的美丽&#xff0c;用她的美色俘获了大明王朝铁血将军洪承畴之心&#xff0c;不仅为满清开国立下了不世之功&#xff0c;而且也打造了一个千古流传的爱情神话。庄妃&#xff0c;孝庄文皇后&#xff0c;博尔济吉特氏&#xff0c;蒙古…

SQLServer2005数据库自动备份

一。SqlServer自动作业备份 1、打开SQL Server Management Studio 2、启动SQL Server代理 3、点击作业->新建作业 4、"常规"中输入作业的名称 5、新建步骤&#xff0c;类型选T-SQL&#xff0c;在下面的命令中输入下面语句 DECLARE strPath NVARCHAR(200)set strP…

JavaScript Array相关方法

JavaScript 标准内置对象 Array常用方法Array.prototype.every()Array.prototype.some()Array.prototype.filter()Array.prototype.find()Array.prototype.findIndex()Array.prototype.indexOf()Array.prototype.includes()Array.prototype.map()其他1. [JavaScript数组去重](h…

Web API之基于H5客户端分段上传大文件

http://www.cnblogs.com/OneDirection/articles/7285739.html 查询很多资料没有遇到合适的&#xff0c;对于MultipartFormDataStreamProvider 也并是很适合&#xff0c;总会出现问题。于是放弃&#xff0c;使用了传统的InputStream 分段处理完之后做merge处理。 前台分段规则 命…

对MySQL进行逻辑卷备份与恢复

ZRM 我之前我介绍过&#xff0c;这里就不多少了。以下是关于用mysql-zrm 来测试 基于LVM 逻辑卷管理的数据库全库备份。我这里用的是SUN 的VBOX 虚拟机来做的测试&#xff0c;基于Red Hat AS 5.3。1. 先建立逻辑卷。fdisk 我就不介绍了&#xff0c;这里演示下怎么用创建逻辑卷以…

医保退费主要流程

1.系统初始化Init GetInvoiceInfo with QryInvoice dobeginClose;ParamByName(DanJuID).AsString:edtDjid.Text;Open;vJiuZhenID:FieldByName(JiuZhenID).AsInteger;GetClinicInfo(vJiuZhenID);//获得就诊信息pnlDjrq.Caption:FieldByName(SerialNo).AsString;pnlSkr.Caption:F…

oo第一单元总结

第一次作业 第一次作业自己虽然很想向着面向对象的方向上写&#xff0c;但写出来还是很C语言式的程序。从头到尾扫描字符串&#xff0c;扫到加减号便认为接下来是一项&#xff0c;再用正则表达式去分情况匹配出这一项。用Hashmap来存储数据&#xff0c;方便合并同类项。最后套一…

npm run build打包失败

使用npm run build命令打包Angular项目时报错&#xff0c;错误信息如下&#xff1a; WARNING in budgets, maximum exceeded for initial. Budget 2 MB was exceeded by 3.33 MB.ERROR in budgets, maximum exceeded for initial. Budget 5 MB was exceeded by 340 kB. npm ER…

YII2 models非常好用的控制输出数据【重写Fields】

models里重写Fields真的很好用&#xff0c;用于分类、评论功能 列子&#xff1a;评论表models/Comment.php 1、关联商品表 2、获取父级&#xff08;即管理员&#xff09;评论 public function Fields()//添加parentComment自定义字段输出 { $fields parent::Fields(); $fi…

Visual studio 2005如何实现源码管理

转自CSDN Visual studio 2005如何实现源码管理(Visual Studio .Net团队开发)目录&#xff1a; 〇、 摘要一、 开发前的准备 二、 创建空的SourceSafe数据库 三、 新建项目并加入版本控制 四、 获取SourceSafe中的项目 五、 版本控制的几个概念 六、 版本控制项目的管理 七、 总…

error while loading shared libraries: libstdc++.so.5: wrong ELF class: ELFCLASS64

今天部署一个探针在运行的时候报了这样一个错&#xff1a;error while loading shared libraries: libstdc.so.5: wrong ELF class: ELFCLASS64 [rootdb152 rma_p_unix]# ldd xxxxlinux-gate.so.1 > (0x00dd7000)libstdc.so.5 > not found # 发现这边动态库找不着 这…

package.json 依赖包版本号

依赖包版本号格式&#xff1a;major.minor.patch major 为主版本号(大版本号)&#xff0c;变化了表示有了一个不兼容上个版本的大更改。 minor 为次版本号(小版本号)&#xff0c;变化了表示增加了新功能&#xff0c;并且可以向后兼容。 patch 为修订版本号&#xff0c;变化了…

.net下绘制统计图工具-请推荐

需要利用到行情、数据频道需要多种样式的表现形式&#xff0c;包括 饼图、柱图、折线图等等 重点是&#xff1a;展示效果好&#xff0c;开发效率高 以前用过dundas chart&#xff0c;不知道有没能生产flash的。 小弟初来乍到&#xff0c;还请给位不吝赐教 放两天置顶&#xff…

wireless(二维数组前缀和)

1 &#xff0e; 无线网络发射器选址(wireless.cpp/c/pas)【问题描述】随着智能手机的日益普及&#xff0c;人们对无线网的需求日益增大。某城市决定对城市内的公共场所覆盖无线网。假设该城市的布局为由严格平行的129条东西向街道和129条南北向街道所形成的网格状&#xff0c;并…

SQL Server 2000 从哪里看是哪个版本

有两种方法&#xff1a; 第一步&#xff1a;使用SQL语句查询 select version 查询结果如下&#xff1a; Microsoft SQL Server 2000 - 8.00.2039 (Intel X86) May 3 2005 23:18:38 Copyright (c) 1988-2003 Microsoft Corporation Personal Edition on Windows NT 5.1 (Build 2…