设计模式系列-创建者模式
一、上篇回顾
上篇我们主要讲述了抽象工厂模式和工厂模式。并且分析了该模式的应用场景和一些优缺点,并且给出了一些实现的思路和方案,我们现在来回顾一下:
抽象工厂模式:一个工厂负责所有类型对象的创建,支持无缝的新增新的类型对象的创建。这种情况是通过配置文件来实现的,通过字典映射的方式来实现,不过可能效率上有点低下,可以通过优化的方式
来做,上篇中我们也给出了委托的工厂实现形式,相比之前的简单工厂模式和工厂模式有了更好的灵活性,并且对具有依赖关系或者组合关系的对象的创建尤为适合。
上篇中,有不少的朋友提出了一些意见和建议,首先很感谢大伙的支持和鼓励,有朋友提出来,我画的图不够专业,专业人士应该用UML建模图来搞,我怎么说呢?我也同意这样的说法,但是我发现我通过
另外的直观的图形,大家一看就能更明白,结合代码,当然好的UML图,已经能表述清楚设计的思路和大体实现了,不过说实话,我看着就有点类,特别是UML图复杂的时候。所以我还是暂时先用这种一般的图
形来表述我理解的设计模式的思想,看看大伙是什么看法和意见,如果说都说说UML图的话,那么后面的相关模式,我会主要以UML专业图来绘制。
我这里总结下我们以后项目中的可能会用到设计模式 之处或者系统架构的时候,一般情况下有这样的几类方案,我们可以在考虑系统的低耦合性的时候的设计:
基本上来说能掌握上面的几类情况,基本上设计出来的系统至少是可用的,不知道大家有没有更多意见和建议。有的请提出来,我会备
注在文章中。
二、摘要
本文主要是针对创建型模式中的创建者模式进行讲述,创建者模式是创建型模式中最负责的一个设计模式了,创建者负责构建一个对象的各个部分,并且完成组装的过程,我们可以这么理解创建
者模式,创建者模式类似与一个步骤基本固定,但是每个步骤中的具体形式却又可以变化的这类对象的创建。也许这样说还是太抽象了,我们这么来理解吧,我感觉让人最容易理解的形式还是图形化
的形式,不但接受起来容易,并且让人映象深刻,不知道大家是不是和我有同感呢?下面我们给出一个简单的例子,通过图形化的流程来说明吧:我们这里以我们大伙平时最常见的做饭为例吧:
可能我这里给出的流程只是我个人理解的或者看到的过程,不代表全部,呵呵,这里只是事例性的说明。
三、本文大纲
a、上篇回顾。
b、摘要。
c、本文大纲。
d、创建者模式的特点及使用场景。
e、创建者模式的实现方案。
f、创建者模式使用总结。
g、系列进度。
h、下篇预告。
四、创建者模式的特点及使用场景
创建者模式主要是用于创建复杂的一些对象,这些对象的创建步骤基本固定,但是可能具体的对象的组成部分却又可以自由的变化,现实中的例子很多,但是可能大伙都比较容易理解的就是,我
们的自己花钱配置的台式机或者笔记本,可以 这样理解,这些硬件设备的各个零件,不管是CPU是Intel的还是AMD的,显卡是华硕的还是小影霸的,不管硬盘是西部数据的还是希捷的,其实想表述
的意思就是对象的具体的组成部分可以是变化的,但是可能我们发现对象的这些组成部分的组织起来的过程是相对固定的,那么我们就可以用创建者模式来做,并且我们引入一个引导者(Director)来
引导这个对象的组装过程。可以简单用图形化的过程来描述如下:
我们上面说明了一个服装的大概生产过程,这里生产的顺序可能会发生变化,或者
生产的帽子,上衣等都会发生变化,但是服装的组装过程基本上变化不大,都是需要帽子,上衣,裤子的,这时候我们可以通过引导着负责组装这样的过程,然后我们在具体的每个部分可以是抽象接
口,根据不同的实现创建不同的帽子来完成变化。
五、创建者模式的实现方案
5.1、经典的创建者模式实现
-
* 我们先给出经典创建者模式的一个实现形式,然后针对这个经典模式后面提出几个改进方案,我们这里以上面讲述的服装的过程作为例子来说明下创建者模式的原理和思想,希望大家也能灵活的运用到实际的项目中去。达到学以致用的目的。
我们来看看具体的代码实现:
/// <summary> /// 创建对象组织的所有构造步骤接口 /// </summary> public interface IBuider { void BuilderPart1(); void BuilderPart2(); void BuilderPart3(); }
定义一个服装对象:
/// <summary> /// 服装对象 /// </summary> public class Dress { /// <summary> /// 构建帽子 /// </summary> public void BuildHat() { throw new NotImplementedException(); } /// <summary> /// 构建上衣 /// </summary> public void BuilderWaist() { throw new NotImplementedException(); } /// <summary> /// 构建裤子 /// </summary> public void BuilderTrousers() { throw new NotImplementedException(); } }
实现创建对象的具体步骤:
public class Builder : IBuider { private Dress _dress; public Builder(Dress dress) { this._dress = dress; }public void BuilderPart1() { this._dress.BuildHat(); }public void BuilderPart2() { this._dress.BuilderWaist(); }public void BuilderPart3() { this._dress.BuilderTrousers(); }public Dress Build() { return this._dress; } }
通过指导者指导对象的创建,而具体的对象的创建还是靠对象本身提供的相应方法,Builder只是调用对象的方法完成组装步骤。Builder内部提供一个返回构造后完整对象的方法,上面给出的方法是
Build()方法。
/// <summary> /// 指导者 /// </summary> public class Director { public void Build(IBuider builder) { builder.BuilderPart1(); builder.BuilderPart2(); builder.BuilderPart3(); } }
通过上面的代码,我们给出了经典创建者模式的核心代码形式,那么针对上面无疑有以下的几个缺点:
1、Ibuilder接口必须定义完整的组装流程,一旦定义就不能随意的动态修改。
2、Builder与具体的对象之间有一定的依赖关系,当然这里可以通过接口来解耦来实现灵活性。
3、Builder必须知道具体的流程。
那么针对上面的几个问题,我们如何来解决呢?我想前面的创建型模式已经给我了足够的经验,还是通过配置文件或者其他的形式来提供灵活性。
针对上面讲述的例子我们可以考虑如下的方式进行改进:
我们先定义一个构造每个对象部分的委托,并且这个方法的参数是动态变化的:
/// <summary> /// 定义通用的构造部分的委托 /// </summary> public delegate void BuildHandler(params object[] items);我们通过定义标记来标识对象中的每个部分的构造步骤
/// <summary> /// 为对象中的每个步骤打上标记 /// </summary> [AttributeUsage(AttributeTargets.Method,AllowMultiple=false)] public class BuildAttribute : Attribute { private MethodInfo hander; private int stepSort;public MethodInfo BuildHandler { get { return this.hander; } set { this.hander = value; } }public int StepSort { get { return this.stepSort; } set { this.stepSort = value; } } }构造对象的统一接口
/// <summary> /// 创建对象组织的所有构造步骤接口 /// </summary> public interface IBuider { void Build<T>() where T : class,new(); }下面给出具体的Builder的缓存实现方案代码
public class CommonBuilder : IBuider { /// <summary> /// 缓存每个对象的具体的构造步骤 /// </summary> private Dictionary<Type, List<BuildHandler>> steps = null;public void Build<T>(T ob) where T : class, new() { //从缓存中读取指定类型的项 List<BuildHandler> handlers = steps[typeof(T)];foreach (BuildHandler handler in handlers) { handler(); } } }给出一些获取某个类型内部的所有具有我们的自定义特性标记的MethodInfo列表
public List<MethodInfo> GetMethodInfoList<T>() where T : class, new() { //从缓存中读取指定类型的项 List<MethodInfo> methods = new List<MethodInfo>();T target = new T(); MethodInfo[] methodList= typeof(T).GetType().GetMethods(); BuildAttribute[] attributes = null; foreach (MethodInfo info in methodList) { attributes= (BuildAttribute[])info.GetCustomAttributes(typeof(BuildAttribute), true); if (attributes.Length > 0) methods.Add(info); }return methods; }获取所有的特性,一般使用这个方法即可获取所有的具有标记该特性的方法列表和相应的步骤:
public List<BuildAttribute> GetBuildAttributeList<T>() where T : class, new() { List<BuildAttribute> attributes = new List<BuildAttribute>(); BuildAttribute[] attributeList = null; BuildAttribute attribute = null; foreach (MethodInfo info in this.methods) { //设置特性中要执行的方法 attributeList = (BuildAttribute[])info.GetCustomAttributes(typeof(BuildAttribute), true); if (attributeList.Length > 0) { attribute = attributeList[0]; attribute.BuildHandler = info; attributes.Add(attribute); } } //缓存步骤 steps.Add(typeof(T), attributes);return attributes; }具体的Build中的调用代码实现:
public T Build<T>(T ob) where T : class, new() { List<BuildAttribute> attributeList = GetBuildAttributeList<T>();T target=new T();//构造对象的过程 foreach (BuildAttribute item in attributeList) { item.BuildHandler.Invoke(target,null); }return target; }这样我们就完成了一个通用的基于标记的自动发现某个类型的标记方法步骤的通用代码实现,可能大家感觉这样的方式还是挺麻烦的,那么我们还有没有更好的改进方案呢?因为每次打标记我还是感
觉挺麻烦的,而且代码量分布的也比较广泛,我想通过统一配置管理的方式,当然也是可以的,那么我们可以通过下面的方式来进行扩展。
配置文件的方式实现创建者,这个怎么说呢,上面的抽象工厂的模式中,我们主要采用了这样的方式来实现配置的灵活性和扩展性,其实创建者也是一样的,我们来看看配置文件吧,我想
就看配置文件就大概知道了,具体的应用代码了,请看下图,粗略描述了实现的思路:
我这里给出配置文件的父子级节点示例:
<?xml version="1.0" encoding="utf-8" ?> <Build> <BuildClass name="ClassName" type="ClassType"> <BuildStep StepOrder="1" MethodName="MethodName1"/> <BuildStep StepOrder="2" MethodName="MethodName2" /> </BuildClass> <BuildClass name="ClassName1" type="ClassType1"> <BuildStep StepOrder="1" MethodName="MethodName1"/> <BuildStep StepOrder="2" MethodName="MethodName2" /> </BuildClass> </Build>我们这里通过在Build实现中读取配置文件中的所有的步骤,放在字典中。给出示例代码
/// <summary> /// 创建对象组织的所有构造步骤接口 /// </summary> public class Buider : IBuider { private Dictionary<Type, List<MethodInfo>> steps = null;public Buider() { steps = new Dictionary<Type, List<MethodInfo>>(); //读取配置文件! //将配置文件中的类名和方法名取出,然后通过反射取到这个类型下的所有方法,根据配置中的步骤和方法名添加到 //步骤列表中,然后缓存到字典中 steps.Add(Type.GetType(""), new List<MethodInfo>()); }public T Build<T>() where T: class,new() { T target = new T();//从字典中找到对应的缓存列表,执行构造过程 List<MethodInfo> list = steps[typeof(T)];//执行构造 foreach (MethodInfo info in list) { info.Invoke(target, null); }return target; } }通过上面的几步配置就可以完成相应的构建过程,这时候我们的指导者的工作就简单了,就是只是简单的通过使用Build中的通用方法
public class Director { public void Build<T>(IBuider builder) where T:class,new() { builder.Build<T>(); } }只要通过上面的步骤就完成了创建者模式的通用实现方案。
六、创建者模式使用总结
通过上面的给出的几类不同的实现方案我们知道,创建者模式是一个对对象的构建过程“精细化”的构建过程,每个部分的构建可能是变化的,但是对象的组织过程是固定的,通过这种统一的创建方
式,无疑增加了我们设计上的灵活性,当我们在构建复杂对象的时候,我们如果发现每个部分可能都是变化的,并且是多个不同的构建步骤的时候,我们可以考虑使用创建者模式。相比我们之前讲述
的工厂和抽象工厂模式区别还是很大的,我们发现创建者适合这类复杂对象的创建,对于抽象工厂可能就无法完成这样的组装工作,而且创建者模式是把复杂对象的内部创建方法进行调用,组织协调
了对象的各个部分前后顺序的控制。简单的描述创建者就是这样的情况:
由于本人水平有限,或者讲述能力有限,表达思路错误或者想法错误,请大家批评指出,如果您有更好的意见或者想法,请提出讨论,欢迎大家提出宝贵意见和建议。
七、系列进度。
创建型
1、系统架构技能之设计模式-单件模式
2、系统架构技能之设计模式-工厂模式
3、系统架构技能之设计模式-抽象工厂模式
4、系统架构技能之设计模式-创建者模式
5、系统架构技能之设计模式-原型模式
结构型
1、系统架构技能之设计模式-组合模式
2、系统架构技能之设计模式-外观模式
3、系统架构技能之设计模式-适配器模式
4、系统架构技能之设计模式-桥模式
5、系统架构技能之设计模式-装饰模式
6、系统架构技能之设计模式-享元模式
7、系统架构技能之设计模式-代理模式
行为型
1、系统架构技能之设计模式-命令模式
2、系统架构技能之设计模式-观察者模式
3、系统架构技能之设计模式-策略模式
4、系统架构技能之设计模式-职责模式
5、系统架构技能之设计模式-模板模式
6、系统架构技能之设计模式-中介者模式
7、系统架构技能之设计模式-解释器模式
八、下篇预告。
下篇将会针对原型模式进行讲述,该模式也是创建型模式中很有特点设计模式之一,该 模式是利用现有的一个对象进行克隆的过程产生一个新的对象,当然这里的复制对象可以是2种,深复制和浅复
制,在这个系列的总结中如果您有好的想法或者创意,请提出来,希望大家多提宝贵意见,错误之处还请指出,请大家继续支持。
转自:https://www.cnblogs.com/hegezhou_hot/archive/2010/12/02/1894771.html
相关文章:
设计模式系列-创建者模式
一、上篇回顾 上篇我们主要讲述了抽象工厂模式和工厂模式。并且分析了该模式的应用场景和一些优缺点,并且给出了一些实现的思路和方案,我们现在来回顾一下: 抽象工厂模式:一个工厂负责所有类型对象的创建,支持无缝的新增新的类型对…...
加工生产调度
题目描述 某工厂收到了 n 个产品的订单,这 n 个产品分别在 A、B 两个车间加工,并且必须先在 A 车间加工后才可以到 B 车间加工。 某个产品 在 A,B 两车间加工的时间分别为 。怎样安排这 n 个产品的加工顺序,才能使总的加工时间…...
Hadoop 集群小文件归档 HAR、小文件优化 Uber 模式
文章目录 小文件归档 HAR小文件优化 Uber 模式 小文件归档 HAR 小文件归档是指将大量小文件合并成较大的文件,从而减少存储开销、元数据管理的开销以及处理时的任务调度开销。 这里我们通过 Hadoop Archive (HAR) 来进行实现,它是一种归档格式…...
Android OkHttp源码阅读详解一
博主前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住也分享一下给大家 👉点击跳转到教程 前言:源码阅读基于okhttp:3.10.0 Android中OkHttp源码阅读二(责任链模式) implementation com.squareup.o…...
UG\NX CAM二次开发 查询工序所在的方法组TAG UF_OPER_ask_method_group
文章作者:代工 来源网站:NX CAM二次开发专栏 简介: UG\NX CAM二次开发 查询工序所在的方法组TAG UF_OPER_ask_method_group 效果: 代码: void MyClass::do_it() { int count=0;tag_t * objects;UF_UI_ONT_ask_selected_nodes(&count, &objects);for (i…...
npm获取函数名称和测试js脚本
这边遇到一个类似于测试的需求,需要从一个js文件里获取函数名,然后尝试执行这些函数和创建实例。这边首先遇到了一个问题是如何动态获取js里的函数名和类名,我这边暂时没找到特别好的方法,已有的方法都是类似于提取语法树那种提取…...
ISO/IEC/ITU标准如何快速查找(三十九)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…...
git私房菜
文章目录 1、公司项目开发Git协作流程2、合并相关的操作3、Git常用命令总结 公司中如何使用Git协同开发的?本文将具体介绍开发模式,以及一些常用命令。 1、公司项目开发Git协作流程 公司一个完整的项目出来,项目的推进是在主分支master上进行…...
docker安装grafana,prometheus,exporter以及springboot整合详细教程(GPE)
springboot项目ip:192.168.168.1 测试服务器ip:192.168.168.81 文章来自互联网,自己略微整理下,更容易上手,方便自己,方便大家 最终效果: node springboot 1.下载镜像 docker pull prom/node-exporter docker pull prom/mysqld-exporter docker pull google/cadvisor dock…...
cka/ckad应试指南 从docker到kubernetes完全攻略
《cka/ckad应试指南 从docker到kubernetes完全攻略》 段超飞 docker 1-安装并配置docker,yum源,docker下载慢 2-基本命令:镜像管理,基本命令,创建容器 3-网络,存储卷,镜像仓库, 4-do…...
js中如何使用可选函数参数
js是网络的核心技术之一。大多数网站都使用它,并且所有现代网络浏览器都支持它,而不需要插件。在本文中,我们将讨论不同的提示和技巧,它们将帮助您进行日常 JavaScript 开发。 在 JavaScript 编码中,您经常需要将函数…...
基于Open3D的点云处理17-Open3d的C++版本
参考: http://www.open3d.org/docs/latest/cpp_api.htmlhttp://www.open3d.org/docs/latest/getting_started.html#chttp://www.open3d.org/docs/release/cpp_project.html#cplusplus-example-projecthttps://github.com/isl-org/open3d-cmake-find-packagehttps:/…...
GIT相关内容总结
Git相关内容总结 Git的功能Git常见命令 Git的功能 Git是版本控制工具。版本控制就是记录你对文件做的所有改动的一个系统,包括改动的内容,改动的时间,改动的备注等,便于你恢复特定的版本。 版本控制系统分为本地版本控制系统&…...
golang清空数组的方法
在Go语言中,数组是固定长度的数据结构,无法直接清空。但是,你可以通过以下两种方法来模拟清空数组的效果: 使用切片(Slicing): 切片是动态长度的,可以用来清空数组。你可以创建一个…...
postgresql并行查询(高级特性)
######################## 并行查询 postgresql和Oracle一样支持并行查询的,比如select、update、delete大事无开启并行功能后,能够利用多核cpu,从而充分发挥硬件性能,提升大事物的处理效率。 pg在9.6的版本之前是不支持的并行查询的,从9.6开始支持并行查询,但是功能非常…...
Python所有方向的学习路线图!!
学习路线图上面写的是某个方向建议学习和掌握的知识点汇总,举个例子,如果你要学习爬虫,那么你就去学Python爬虫学习路线图上面的知识点,这样学下来之后,你的知识体系是比较全面的,比起在网上找到什么就学什…...
2022年03月 C/C++(七级)真题解析#中国电子学会#全国青少年软件编程等级考试
C/C++编程(1~8级)全部真题・点这里 第1题:红与黑 有一间长方形的房子, 地上铺了红色、 黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上, 只能向相邻的黑色瓷砖移动。 请写一个程序, 计算你总共能够到达多少块黑色的瓷砖。 时间限制: 1000 内存限制: 65536 输入…...
使用 Laf 一周内上线美术狮 AI 绘画小程序
“美术狮 AI 绘画”(以下简称“美术狮”),是我们小团队的一次尝试,定位是人人都可以上手的,充满创意的,理解中文和中国文化的图片生成工具。 在完善图像模型和论证核心问题之后,我们开始构建 MV…...
Kubernetes(k8s)当中安装并使用ingress暴露应用
Kubernetes当中安装并使用ingress暴露应用 为什么需要Ingress前期准备集群准备LoadBalancer准备 安装Ingress-Nginx下载地址v1.3.1v1.8.1 修改文件v1.3.1v1.8.1修改ingress服务类型配置 执行安装 部署应用通过ingress-nginx暴露应用部署ingress的yaml文件v1.3.1v1.8.1 为什么需…...
03-Flask-工程配置加载方式
工程配置加载方式 前言配置对象中加载配置文件中加载环境变量中加载三种配置方式优缺点工厂模式创建Flask app 前言 本篇来学习下Flake工程配置加载方式 配置对象中加载 应用场景:作为默认配置写在代码中 # -*- coding: utf-8 -*- # Time : 2023/9/2 # Autho…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

基本上来说能掌握上面的几类情况,基本上设计出来的系统至少是可用的,不知道大家有没有更多意见和建议。有的请提出来,我会备
可能我这里给出的流程只是我个人理解的或者看到的过程,不代表全部,呵呵,这里只是事例性的说明。
我们上面说明了一个服装的大概生产过程,这里生产的顺序可能会发生变化,或者