系统架构技能之设计模式-组合模式

一、上篇回顾

我们上篇主要讲述了结构型模式中的外观模式,外观模式作为结构型模式中的一个简单又实用的模式,外观模式通过封装细节来提供大粒度的调用,

直接的好处就是,封装细节,提供了应用写程序的可维护性和易用性。外观模式一般应用在系统架构的服务层中,当我们是多个不同类型的客户端应用程序

时,比如一个系统既可以在通过Web的形式访问,也可以通过客户端应用程序的形式时,可能通过外观模式来提供远程服务,让应用程序进行远程调用,

这样通过外观形式提供服务,那么不管是什么样的客户端都访问一致的外观服务,那么以后就算是我们的应用服务发生变化,那么我们不需要修改没一个客

户端应用的调用,只需要修改相应的外观应用即可。

我们主要是讲述了以下的几种情况,使用外观模式可能更适合:

1、我们在使用第三方类库或者API的时候,我们通过本地的API接口的封装,来完成对第三方API接口的粗粒度外观对象,通过这个外观对象可以

很容易的完成服务的调用。

2、我们在架构设计的过程中,一次的功能访问可能需要同时的调用很多个对象,那么如果我们在服务调用的时候,能够在应用程序调用中一次就

能完成所有要同时调用的对象那该多好啊,外观模式无疑是最好的原则,特别是在分布式应用中,通过远程调用服务,通过外观模式降低应用程序与服务的

交互次数,同时可以降低应用程序的复杂性。

二、摘要

本文将会讲述结构性模式中的另外一个常用的模式-组合模式,我们平时在面向对象的设计中,我想有一个原则经常被提及就是,我们在设计的时候,

对象组合>类的继承,本篇将会将结合简单的实例来说明这方面的优势,并且完成对组合模式的主题思想的掌握。我们这样来简单的理解组合模式,组合模

式就是把一些现有的对象或者元素,经过组合后组成新的对象,新的对象提供内部方法,可以让我们很方便的完成这些元素或者内部对象的访问和操作。我

们也可以把组合对象理解成一个容器,容器提供各种访问其内部对象或者元素的API,我们只需要使用这些方法就可以操作它了。那么对象组合相比继承的

优势有哪些呢?可能具体的优势,也不是一句二句就能表述清楚的,还是我们来看看图形的可视化的描述吧。

clip_image002我们这里设计的是持久化服务写到一个基类中,

然后继承自该基类的子类都会拥有内置的持久化方法,可能后续我们又要添加其他的针对某个具体的对象类,有一些个性化的服务,我们通过扩展这个类,

进行继承,这样多重继承后,会有一个很大的问题。子类膨胀的问题,而且一般来说继承的重数达到5层左右的时候,性能上可能就会有一定的瓶颈,。也

不是好的设计的思路。这时候对象组合可能为我们提供了更好的解决方案。基于组合方式的话,可能我们可以这样来做,换个思路:就像我们的一个负责的

对象,可以通过简单的对象来组成的道理差不多,其实。

clip_image004通过上图,我们知道了,组合对象可以

看作是一系列简单的对象组合成负责的对象的一个模式,复杂对象可以看作是简单对象的一个容器。这个复杂对象完成了简单对象的封装,通过这个容器完

成对象内部简单对象的访问。

三、本文大纲

a、上篇回顾。

b、摘要。

c、本文大纲。

d、组合模式的特点及使用场景。

e、组合模式的经典实现。

f、组合模式的其他方案。

g、原型模式使用总结。

h、系列进度。

i、下篇预告。

四、组合模式的特点及使用场景

组合模式是将一系列对象组合成树形结构用来表示整体和部分之间的关系,组合模式的主要目的是达到,访问组合对象和访问单个对象具有一致性。这里的组合对象比较特殊,本身他可以是由其他的对象组合而成,同时,这个组合对象又可以是组成更复杂对象的一个部分。我们来举个例子来说明吧,可能更直观。

clip_image006这里我们可以理解为一个简单的查询组件可能有要满足

上述的几类查询条件的输入类型,将这个组件作为一个容器,那么同事,这个容器又可以作为另外一个容器的组件来运行,那么我们又可以在分页控件中,

将这个查询组件包含到其中。以为分页提供相应的查询元素的集成。

我们一般在如下场景中使用组合模式比较方便:

1、我们有的时候想用户使用一个复杂对象像使用简单对象一样的方式去访问,并且用户同意使用对象内部的所有的对象时,我们可以考虑使用该模

式。这个怎么理解呢?我们使用复杂对象像使用简单对象一样的方式去访问的话,那么我们可以使用组合对象,我们把这些简单的对象进行组合,用组合对

象进行包装,并且提供相应的操作组合对象内部的方法。结合上图中的例子,我们可以这样理解,我们把查询组件封装成一个用户控件,然后可以在其他的

页面中进行调用。这个时候,我们可能考虑如何分部页面,如何能够动态的维护界面上的控件,是否显示,控件里面显示的文本和值是什么?等等,这些都

是我们可以考虑的元素,那么我们如何来做呢?提供下面的几个方法。我还是给出图来说话吧:

clip_image008包含上面的这些元素,那么我们可以通过一些相应的方法来进行访

问内部的元素。我们给出简单的示例代码:

public QueryControl()
{
InitializeComponent();

this.InitControlList();
}

public event EventHandler handler;

private Dictionary<string,Control> _controlList = null;
///
/// 初始化控件信息
///
private void InitControlList()
{
this._controlList = new Dictionary<string,Control>();
}
///
/// 返回界面控件中所有的查询条件控件列表
///
///
public Dictionary<string,Control> GetControls()
{
return this._controlList;
}

///
/// 添加查询控件到界面中
///
///
///
public bool AddControl(string key,Control control)
{
if(this._controlList.ContainsKey(key))
return false;
this._controlList.Add(key,control);

return true;
}

///
/// 移除指定键值的对象
///
///
///
public bool RemoveControl(string key)
{
if (this._controlList.ContainsKey(key))
return false;
this._controlList.Remove(key);

return true;
}

public virtual void OnQuery()
{
if (this.handler != null)
handler(this, new EventArgs());
}

public void Query(object sender, EventArgs e)
{
this.CreateSQL();
this.OnQuery();
}

///
/// 根据选中的条件生成SQL语句
///
private void CreateSQL()
{
throw new NotImplementedException();
}

上面的代码很简单就是给出了基本的思路,并不是完整的功能。我们来看其他的可能会用到组合模式的情况

2、如果有的时候,我们希望用户不了解自己使用的对象有多复杂,并且组合对象可以的内部可以自由的变化和组合,但是不会影响到客户应用程序使用这

个组合对象,如果项目中需要新增一个组合对象的时候,客户调用的程序还是一样,不会因为这个组合对象发生变更而发生变化。组合模式在解决整体和部

分之间的问题应用很广泛,也可以降低系统的复杂度,因为我们可以把复杂的组件看作另一个组件的组成部分来处理。就像上面的查询组件,那么我们可以

把查询组件作为分页组件的一个部分来处理。我这里就不给出示例代码了通过上面的情况,我们可以大概的知道,组合模式的使用场景。下面我们就要结合

实例说明组合模式的用处了。

五、组合模式的经典实现

我们先来看看使用经典的组合模式来实现我们上面说的查询控件吧,看看和其他的方案有什么样的不同,不过大体的思路都是一致的,把其他的对象

进行组合成复杂的对象,然后提供操作内部对象的方法,具体的方式可以很多。我们这里还是以上面的查询组件和分页组件为例来说明具体的实现思路,当

然我这里只是给出核心代码,但不是完整的代码:

我们先给出控件内部的完整定义:

public QueryPanel()
{
InitializeComponent();

this.InitControlList();
}

public event EventHandler handler;

private Dictionary<string,Control> _controlList = null;
///
/// 初始化控件信息
///
private void InitControlList()
{
this._controlList = new Dictionary<string,Control>();
}
///
/// 返回界面控件中所有的查询条件控件列表
///
///
public Dictionary<string,Control> GetControls()
{
return this._controlList;
}

///
/// 添加查询控件到界面中
///
///
///
public bool AddControl(string key,Control control)
{
if(this._controlList.ContainsKey(key))
return false;
this._controlList.Add(key,control);

return true;
}

///
/// 移除指定键值的对象
///
///
///
public bool RemoveControl(string key)
{
if (this._controlList.ContainsKey(key))
return false;
this._controlList.Remove(key);

return true;
}

public virtual void OnQuery()
{
if (this.handler != null)
handler(this, new EventArgs());
}

public void Query(object sender, EventArgs e)
{
this.CreateSQL();
this.OnQuery();
}

///
/// 根据选中的条件生成SQL语句
///
private void CreateSQL()
{
throw new NotImplementedException();
}

public virtual Control this[string key]
{
get
{
return this._controlList[key];
}
set
{
this._controlList[key] = value;
}
}

public virtual IEnumerable GetControlList()
{
if (this._controlList.Count > 0)
{
foreach (Control control in this._controlList.Values)
{
foreach (Control item in control.Controls)
{
yield return control;
}
}
}
}

这时候我们如果想要重写上面的方案,可能有时候我们的查询控件需要基于上面的几个基本的操作方法进行重写,完成自定义的组合对象的操作:

public partial class QueryPanelText : QueryPanel
{
public QueryPanelText()
{
InitializeComponent();
}

public override Control this[string key]
{
get
{
return base.GetControls()[key];
}
set
{
base.GetControls()[key] = value;
}
}

///
/// 获取集合中的所有控件集合
///
///
public override IEnumerable GetControlList()
{
return base.GetControlList();
}
}

当然我上面并没有改变对象的任何行为,这里定义了公共的行为,有的时候我们需要限制一些对象内部对象的某些行为,这时候我们通过继承公共的

对象来重写对象的行为来完成。例如从组合对象衍生出来的某些组合对象,可能不具有父类的某些行为时,我们可以通过重写父类的行为来完成自定义的操

作。

六、组合模式的其他方案

6.1、通过过滤器来改进上述方案。

有的时候,我们可能并不关心对象中的所有的元素,我们只是操作其中的具有同类特征的对象,比如对于上面的查询控件中的对象,我们可能想要获

取设置条件的控件项,或者说我们相应控制文本框或者下拉框的样式或者高度等等,这个时候,我们需要对组合对象内的元素就行筛选。这个时候,我们可

能可以采用过滤器来实现这样的功能。

对于上面的结果,我们可能需要获取界面中所有的文本框,或者下拉框,或者其他类型的控件等,这时候我们可以如下方式来做,通过接口定义,根

据传入的不同的过滤器,返回过滤后的结果集合。我们先定义一个过滤器接口:

public interface ISelectRule
{
bool IsMatch(Control control);
}

我们这里定义一个文本过滤器的实现。

public class TextSelect : ISelectRule
{
#region ISelectRule 成员

public bool IsMatch(System.Windows.Forms.Control control)
{
if (control is System.Windows.Forms.TextBox)
return true;

return false;
}

#endregion
}

我们来看看具体的查询控件内部的获取内部元素的方法:

///
/// 获取集合中的所有控件集合
///
///
public IEnumerable GetControlList(ISelectRule rule)
{
foreach (Control control in base.GetControls().Values)
{
if (rule.IsMatch(control))
yield return control;
}
}

通过上面的几行代码的改进,我们就可以完成对内部的组合对象的改进,这样就可以完成根据不同的查询规则,来返回不同的内部对象集合。

通过上面的迭代器来封装我们的内部组合对象,我们通过迭代器来完成组合对象的过滤。我们还可以通过XML文件来组织我们的对象组合的结构,XML文

件先天性的优势就是结构化的语言,特别适合这样的整体与局部关系的结构,所以我们的组合对象也可以通过XML文件来组织,可以将复杂对象设置为一

个复杂的节点,该节点下包含多个对象时,我们只需要将每个对象设置为一个节点,通过XPath语言完成查询。

6.2XML文件格式的组合模式

使用XML文件来完成组合模式有如下优点:

1、我们有系统提供的类库自动完成对XML文件的操作和访问。

2、XML提供了基于XPath解析的查询语言。

也有一些点,就是操作XML文件,调试起来会比较麻烦,而且进行代码编写和测试是个不太方便的事情。

我们来看看如果我们把上面的获取迭代器的代码,还原成XML文件的格式该如何书写呢?

<?xml version="1.0" encoding="utf-8" ?>

对于上面的XML文件,那么我们如何获取到这个XML文件中的某些类型的节点呢?比如我如何获取到TextBox呢?这时候我们可以使用Xpath语法来进行

选择:

public class XMLSelect
{
string xPath = “/Composite/QueryPanel/TextBox”;
private XmlNodeList list = null;
public XmlNodeList GetNodes()
{
System.Xml.XmlDocument doc = new XmlDocument();
doc.LoadXml(“Composite.xml”);

list = doc.SelectNodes(xPath);

return list;
}

public List GetControls()
{
List listControls = new List();

foreach (XmlNode node in list)
{
System.Windows.Forms.TextBox textBox = new TextBox();
textBox.Name = node.Attributes[“name”].Value.ToString();

listControls.Add(textBox);
}

return listControls;
}
}

基于上面的形式我们也可以完成组合模式的要求,通过上面的讲解,我们应该对组合模式有了一定的了解。

七、组合模式使用总结

通过上面的简单讲解,我们知道了,组合模式意图是通过整体与局部之间的关系,通过树形结构的形式进行组织复杂对象,屏蔽对象内部的细节,对

外展现统一的方式来操作对象,是我们处理更复杂对象的一个手段和方式。本文以查询控件为例,说明了,查询控件内部的组成元素,及如何操作内部的组

成元素,包括添加元素,删除和处理相应事件的Handler,当然组合模式的作用远比这些强大,后面我们肯定会在一些实例代码中运用到组合模式的。组合

模式如果在条件允许的情况下,我们尽量使用组合模式来处理复杂对象,远比通过继承出来的对象来的有效。

由于本人水平有限,加之理解有误,错误之处还请大家批评指出,多谢大伙的支持和关注!

八、系列进度

创建型

1、系统架构技能之设计模式-单件模式

2、系统架构技能之设计模式-工厂模式

3、系统架构技能之设计模式-抽象工厂模式

4、系统架构技能之设计模式-创建者模式

5、系统架构技能之设计模式-原型模式

结构型

1、系统架构技能之设计模式-组合模式

2、系统架构技能之设计模式-外观模式

3、系统架构技能之设计模式-适配器模式

4、系统架构技能之设计模式-桥模式

5、系统架构技能之设计模式-装饰模式

6、系统架构技能之设计模式-享元模式

7、系统架构技能之设计模式-代理模式

行为型

1、系统架构技能之设计模式-命令模式

2、系统架构技能之设计模式-观察者模式

3、系统架构技能之设计模式-策略模式

4、系统架构技能之设计模式-职责模式

5、系统架构技能之设计模式-模板模式

6、系统架构技能之设计模式-中介者模式

7、系统架构技能之设计模式-解释器模式

九、下篇预告

下篇,我们将会讲述结构性模式中的适配器模式,我想大家对适配器模式应该有不少的了解,因为如果我们经常在项目中使用第三方的或者因为旧的系统与新系

统的接口类型不一致,倒置无法进行调用,这时候我们可以通过适配器模式将旧的接口,转化成可以让我们的新系统调用的接口形式,完成调用,很神奇吧!下篇我们

将会进行详细的阐述。

转自:https://www.cnblogs.com/hegezhou_hot/archive/2010/12/06/1898161.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/123068.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十二、集合(4)

本章概要 集合 Set映射 Map队列 Queue 优先级队列 PriorityQueue 集合与迭代器 集合Set Set 不保存重复的元素。 如果试图将相同对象的多个实例添加到 Set 中&#xff0c;那么它会阻止这种重复行为。 Set 最常见的用途是测试归属性&#xff0c;可以很轻松地询问某个对象是否…

命名空间的详讲

本篇文章旨在讲解C中命名空间的概念以及其相关注意事项&#xff01; C的介绍 C作为C语言的衍生&#xff0c;其对C语言中的一些缺陷进行了一些的补充和优化。但是C也对C语言具有兼容性&#xff01; 本文旨在讲解C对C语言中当声明的变量与库函数的一些标识符&#xff0c;关键字…

【数论】容斥问题教程+符号含义+公式(c++)

容斥问题是什么 比如我们平常考试&#xff0c;我们会统计有几个语文及格&#xff0c;有几个数学及格&#xff0c;比如5个语文及格&#xff0c;2个数学及格&#xff0c;当然了&#xff0c;也会有大学霸两科都及格&#xff0c;比如1个人语文数学都及格&#xff0c;那我们班上一共…

Hadoop的第二个核心组件:MapReduce框架第四节

Hadoop的第二个核心组件&#xff1a;MapReduce框架 十、MapReduce的特殊应用场景1、使用MapReduce进行join操作2、使用MapReduce的计数器3、MapReduce做数据清洗 十一、MapReduce的工作流程&#xff1a;详细的工作流程第一步&#xff1a;提交MR作业资源第二步&#xff1a;运行M…

vnc与windows之间的复制粘贴

【原创】VNC怎么和宿主机共享粘贴板 假设目标主机是linux&#xff0c;终端主机是windows&#xff08;就是在windows上使用VNC登陆linux&#xff09; 在linux中执行 vncconfig -nowin& 在linux选中文字后&#xff0c;无需其他按键&#xff0c;直接在windows中可以黏贴。 …

【C++基础】5. 变量作用域

文章目录 【 1. 局部变量 】【 2. 全局变量 】【 3. 局部变量和全局变量的初始化 】 作用域是程序的一个区域&#xff0c;一般来说有三个地方可以定义变量&#xff1a; 在函数或一个代码块内部声明的变量&#xff0c;称为局部变量。 在函数参数的定义中声明的变量&#xff0c;称…

【前端demo】圣诞节灯泡 CSS动画实现轮流闪灯

文章目录 效果过程灯泡闪亮实现&#xff08;animation和box-shadow&#xff09;控制灯泡闪亮时间和顺序&#xff08;animation-delay&#xff09;按钮开关 代码htmlcssjs 参考代码1代码2 前端demo目录 效果 效果预览&#xff1a;https://codepen.io/karshey/pen/zYyBRWZ 参考…

Vue + Element UI 前端篇(十二):用户管理模块

Vue Element UI 实现权限管理系统 前端篇&#xff08;十二&#xff09;&#xff1a;用户管理模块 用户管理模块 添加接口 在 http/moduls/user.js 中添加用户管理相关接口。 import axios from ../axios/* * 用户管理模块*/// 保存 export const save (params) > {ret…

React中消息订阅与发布(PubSubJS)——两个组件之间通信

结合案例&#xff1a;github搜索案例 结果如下图 1.父容器代码 import React, { Component } from react import Search from ./components/Search import List from ./components/List export default class App extends Component {render() {return (<div className&…

【面试题】如何实现数组去重的?有几种方式?

前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 【国庆头像】- 国庆爱国 程序员头像&#xff01;总有一款适合你&#xff01; 1. 方法一&#xff1a;利用两层循环数组的splice方法 通过两层循环对数组…

zabbix监控平台部署

目录 前言 一、zabbix的基本概述 &#xff08;一&#xff09;、zabbix的工作流程 &#xff08;二&#xff09;、zabbix的构成 &#xff08;三&#xff09;、zabbix的监控对象 &#xff08;四&#xff09;、zabbix的常用术语 &#xff08;五&#xff09;、zabbix进程详解…

MySQL数据库的增删改查(进阶)

目录 数据库约束 约束类型 NULL约束 UNIQUE&#xff1a;唯一约束 DEFAULT&#xff1a;默认值约束 PRIMARY KEY&#xff1a;主键约束 FOREIGN KEY&#xff1a;外键约束 表的设计 一对一关系 一对多关系 多对多关系 查询 聚合查询 聚合函数 GROUP BY子句 HAVING …

深入协议栈了解TCP的三次握手、四次挥手、CLOSE-WAIT、TIME-WAIT。

TCP网络编程的代码网上很多&#xff0c;这里就不再赘述&#xff0c;简单用一个图展示一下tcp网络编程的流程&#xff1a; 1、深入connect、listen、accept系统调用&#xff0c;进一步理解TCP的三次握手 这三个函数都是系统调用&#xff0c;我们可以分为请求连接方和被…

项目(智慧教室)第四部分,页面交互功能

一。页面构思 1.标题栏 大标题&#xff1a;智慧教室管理系统 小标题&#xff1a;灯光&#xff0c;报警&#xff0c;风扇&#xff0c;温度&#xff0c;湿度&#xff0c;光照 2.样式设计 背景设置。字体设置&#xff08;字体大小&#xff0c;格式&#xff0c;颜色&#xff09; 3.…

简单使用_matlab生成数据帧

文章目录 生成数据帧参考 生成数据帧 代码如下&#xff0c;代码很简单&#xff0c;有几点要注意&#xff0c; 较高版本的MATLAB中支持0x的写法使用bitand进行位运算使用strcat函数进行字符串拼接时&#xff0c;如果需要插入空格&#xff0c;要使用双引号 cmd_ay(1) 0x33; …

【跟小嘉学 Rust 编程】二十三、Cargo 使用指南

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…

【MySQL】CRUD (增删改查) 基础

CRUD&#xff08;增删改查&#xff09;基础 一. CRUD二. 新增 &#xff08;Create&#xff09;1. 单行数据 全列插入2. 多行数据 指定列插入 三. 查询&#xff08;Retrieve&#xff09;1. 全列查询2. 指定列查询3. 查询字段为表达式4. 别名5. 去重&#xff1a;DISTINCT6. 排序…

K210学习笔记——三角函数下的目标追踪

各位正在读文章的朋友们你们好&#xff0c;本人为非专业学生&#xff0c;如有不对的地方&#xff0c;期待您的指正。 目标追踪的意思是&#xff1a;识别到目标物体&#xff0c;通过舵机转动&#xff0c;朝向目标物体. 实验器材&#xff1a;二自由度舵机云台加两个SG90舵机&…

Vue2项目练手——通用后台管理项目第四节

Vue2项目练手——通用后台管理项目 数据的请求mock数据模拟实战文件目录src/api/mock.jssrc/api/mockServeData/home.jsmain.js 首页组件布局可视化图表可视化图表布局Home.vue echarts表Home.vue 数据的请求 mock数据模拟实战 mock官方文档 前端用来模拟后端接口的工具&…

uni-app语音转文字功能demo(同声传译)

目录 首先去微信开发者官网申请一下同声传译的插件 微信公众平台 在文件中开始引用&#xff1a; 首先去微信开发者官网申请一下同声传译的插件 微信公众平台 后续使用的时候可以看详情里面的信息进行使用 在文件中开始引用&#xff1a; 注意&#xff01;&#xff01;在这个…