设计模式系列-创建者模式

一、上篇回顾

上篇我们主要讲述了抽象工厂模式和工厂模式。并且分析了该模式的应用场景和一些优缺点,并且给出了一些实现的思路和方案,我们现在来回顾一下:

抽象工厂模式:一个工厂负责所有类型对象的创建,支持无缝的新增新的类型对象的创建。这种情况是通过配置文件来实现的,通过字典映射的方式来实现,不过可能效率上有点低下,可以通过优化的方式

来做,上篇中我们也给出了委托的工厂实现形式,相比之前的简单工厂模式和工厂模式有了更好的灵活性,并且对具有依赖关系或者组合关系的对象的创建尤为适合。

上篇中,有不少的朋友提出了一些意见和建议,首先很感谢大伙的支持和鼓励,有朋友提出来,我画的图不够专业,专业人士应该用UML建模图来搞,我怎么说呢?我也同意这样的说法,但是我发现我通过

另外的直观的图形,大家一看就能更明白,结合代码,当然好的UML图,已经能表述清楚设计的思路和大体实现了,不过说实话,我看着就有点类,特别是UML图复杂的时候。所以我还是暂时先用这种一般的图

形来表述我理解的设计模式的思想,看看大伙是什么看法和意见,如果说都说说UML图的话,那么后面的相关模式,我会主要以UML专业图来绘制。

我这里总结下我们以后项目中的可能会用到设计模式 之处或者系统架构的时候,一般情况下有这样的几类方案,我们可以在考虑系统的低耦合性的时候的设计:

image基本上来说能掌握上面的几类情况,基本上设计出来的系统至少是可用的,不知道大家有没有更多意见和建议。有的请提出来,我会备

注在文章中。

二、摘要

本文主要是针对创建型模式中的创建者模式进行讲述,创建者模式是创建型模式中最负责的一个设计模式了,创建者负责构建一个对象的各个部分,并且完成组装的过程,我们可以这么理解创建

者模式,创建者模式类似与一个步骤基本固定,但是每个步骤中的具体形式却又可以变化的这类对象的创建。也许这样说还是太抽象了,我们这么来理解吧,我感觉让人最容易理解的形式还是图形化

的形式,不但接受起来容易,并且让人映象深刻,不知道大家是不是和我有同感呢?下面我们给出一个简单的例子,通过图形化的流程来说明吧:我们这里以我们大伙平时最常见的做饭为例吧:

image可能我这里给出的流程只是我个人理解的或者看到的过程,不代表全部,呵呵,这里只是事例性的说明。

三、本文大纲

a、上篇回顾。

b、摘要。

c、本文大纲。

d、创建者模式的特点及使用场景。

e、创建者模式的实现方案。

f、创建者模式使用总结。

g、系列进度。

h、下篇预告。

四、创建者模式的特点及使用场景

创建者模式主要是用于创建复杂的一些对象,这些对象的创建步骤基本固定,但是可能具体的对象的组成部分却又可以自由的变化,现实中的例子很多,但是可能大伙都比较容易理解的就是,我

们的自己花钱配置的台式机或者笔记本,可以 这样理解,这些硬件设备的各个零件,不管是CPU是Intel的还是AMD的,显卡是华硕的还是小影霸的,不管硬盘是西部数据的还是希捷的,其实想表述

的意思就是对象的具体的组成部分可以是变化的,但是可能我们发现对象的这些组成部分的组织起来的过程是相对固定的,那么我们就可以用创建者模式来做,并且我们引入一个引导者(Director)来

引导这个对象的组装过程。可以简单用图形化的过程来描述如下:

image我们上面说明了一个服装的大概生产过程,这里生产的顺序可能会发生变化,或者

生产的帽子,上衣等都会发生变化,但是服装的组装过程基本上变化不大,都是需要帽子,上衣,裤子的,这时候我们可以通过引导着负责组装这样的过程,然后我们在具体的每个部分可以是抽象接

口,根据不同的实现创建不同的帽子来完成变化。

五、创建者模式的实现方案

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必须知道具体的流程。

那么针对上面的几个问题,我们如何来解决呢?我想前面的创建型模式已经给我了足够的经验,还是通过配置文件或者其他的形式来提供灵活性。

  • 5.2、创建者模式特性+委托实现

针对上面讲述的例子我们可以考虑如下的方式进行改进:

我们先定义一个构造每个对象部分的委托,并且这个方法的参数是动态变化的:

    /// <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; }

这样我们就完成了一个通用的基于标记的自动发现某个类型的标记方法步骤的通用代码实现,可能大家感觉这样的方式还是挺麻烦的,那么我们还有没有更好的改进方案呢?因为每次打标记我还是感

觉挺麻烦的,而且代码量分布的也比较广泛,我想通过统一配置管理的方式,当然也是可以的,那么我们可以通过下面的方式来进行扩展。

  • 5.3、创建者模式配置文件方式实现

配置文件的方式实现创建者,这个怎么说呢,上面的抽象工厂的模式中,我们主要采用了这样的方式来实现配置的灵活性和扩展性,其实创建者也是一样的,我们来看看配置文件吧,我想

就看配置文件就大概知道了,具体的应用代码了,请看下图,粗略描述了实现的思路:

image

我这里给出配置文件的父子级节点示例:

<?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>(); } }

只要通过上面的步骤就完成了创建者模式的通用实现方案。

六、创建者模式使用总结

通过上面的给出的几类不同的实现方案我们知道,创建者模式是一个对对象的构建过程“精细化”的构建过程,每个部分的构建可能是变化的,但是对象的组织过程是固定的,通过这种统一的创建方

式,无疑增加了我们设计上的灵活性,当我们在构建复杂对象的时候,我们如果发现每个部分可能都是变化的,并且是多个不同的构建步骤的时候,我们可以考虑使用创建者模式。相比我们之前讲述

的工厂和抽象工厂模式区别还是很大的,我们发现创建者适合这类复杂对象的创建,对于抽象工厂可能就无法完成这样的组装工作,而且创建者模式是把复杂对象的内部创建方法进行调用,组织协调

了对象的各个部分前后顺序的控制。简单的描述创建者就是这样的情况:

image

由于本人水平有限,或者讲述能力有限,表达思路错误或者想法错误,请大家批评指出,如果您有更好的意见或者想法,请提出讨论,欢迎大家提出宝贵意见和建议。

七、系列进度。

创建型

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

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

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

相关文章

kotlin 转 Java

今天突然想研究下有些kotlin文件转为Java到底长什么样&#xff0c;好方便优化kotlin代码&#xff0c;搞了半天发现一个非常简单的Android Studio或者Intellij idea官方插件Kotlin&#xff0c;Kotlin是插件的名字&#xff0c;真是醉了&#xff1b; 这里以AS为例&#xff0c;使用…

pnpm快速创建 Vue.js 项目(npm类似)

目录 pnpm 创建一个 Vue.js 项目 前提准备&#xff1a; 运行创建命令&#xff1a; 选择项目配置&#xff1a;&#xff08;按需选择&#xff09; cd 项目名&#xff1a;&#xff08;进入项目终端&#xff09; 安装项目依赖&#xff1a; 运行项目&#xff1a; pnpm 创建一…

枚举的简单介绍

目录 概念&#xff1a; 枚举的声明&#xff1a; 枚举的使用&#xff1a; 枚举的取值&#xff1a; 枚举的优点&#xff1a; #define的功能&#xff1a; 而与#define对比&#xff0c;枚举的优点有&#xff1a; 概念&#xff1a; 枚举顾名思义就是⼀⼀列举。 把可能的取值…

无涯教程-JavaScript - VARP函数

VARP函数取代了Excel 2010中的VAR.P函数。 描述 该函数根据整个总体计算方差。 语法 VARP (number1,[number2],...)争论 Argument描述Required/OptionalNumber1The first number argument corresponding to a population.RequiredNumber2...Number arguments 2 to 255 cor…

以GitFlow分支模型为基准的Git版本分支管理流程

以GitFlow分支模型为基准的Git版本分支管理流程 文章目录 以GitFlow分支模型为基准的Git版本分支管理流程GitFlow分支模型中的主要概念GitFlow的分支管理流程图版本号说明借助插件Git Flow Integration Plus实现分支模型管理其他模型TBD模型阿里AoneFlow模型 GitFlow分支模型中…

R语言数据管理

1.将变量加入列表中 > mydata<-data.frame(x1c(2,3,4,5),x2c(2,5,7,9)) > mydatax1 x2 1 2 2 2 3 5 3 4 7 4 5 9 > sumx<-x1x2 Error: object x1 not found > sumx<-mydata$x1mydata$x2 > sumx [1] 4 8 11 14 > ls() [1] "mydata&…

小小一个设置程序高级感拉满

手动设置 结果如下 代码设置&#xff1a; procedure TForm1.Button1Click(Sender: TObject); begin TStyleManager.TrySetStyle(cbxVclStyles.Text); end;procedure TForm1.FormCreate(Sender: TObject); var StyleName:string; begin for StyleName in TStyleManager.StyleNa…

java线程状态

图形说明: Thread.State源码注释: public enum State {/*** 新生状态&#xff1a;线程对象创建&#xff0c;但是还未start()*/NEW,/*** 线程处于可运行状态&#xff0c;但是这个可运行状态并不代表线程一定在虚拟机中执行。* 需要等待从操作系统获取到资源(比如处理器时间片…

Spark-Core核心算子

文章目录 一、数据源获取1、从集合中获取2、从外部存储系统创建3、从其它RDD中创建4、分区规则—load数据时 二、转换算子(Transformation)1、Value类型1.1 map()_1.2 mapPartitions()1.3 mapPartitionsWithIndex(不常用)1.4 filterMap()_扁平化&#xff08;合并流&#xff09;…

Java面试之斐波那契数列(Fibonacci)及其应用:青蛙跳台阶问题

文章目录 一、斐波那契数列问题1.1 题目1.2 什么是斐波那契数列1.3 效率很低的解法&#xff1a;递归1.4 递归缺点分析 二、比较好的解决办法2.1 保存数列中间项2.2 从下往上计算 三、公式法四、青蛙跳台阶问题4.1 题目及分析4.2 代码实现 一、斐波那契数列问题 1.1 题目 写一…

Paddle训练COCO-stuff数据集学习记录

COCO-stuff数据集 COCO-Stuff数据集对COCO数据集中全部164K图片做了像素级的标注。 80 thing classes, 91 stuff classes and 1 class ‘unlabeled’ 数据集下载 wget --directory-prefixdownloads http://images.cocodataset.org/zips/train2017.zip wget --directory-prefi…

XXL-JOB 分布式任务调度平台

目录 背景 项目架构 核心流程1——执行器自动注册 核心流程2——调度任务 特性——分片广播 背景 为什么需要任务调度平台? 单机定时任务 Java中传统的定时任务实现方案&#xff0c;比如JDK 1.3 提供的 Timer、JDK 1.5 提供的 ScheduledExecutorService、Spring 3.0 提…

huggingface下载模型文件(基础入门版)

huggingface是一个网站&#xff0c;类似于github&#xff0c;上面拥有众多开源的模型、数据集等资料&#xff0c;人工智能爱好者可以很方便的上面获取需要的数据&#xff0c;也可以上传训练好的模型&#xff0c;制作的数据集等。本文只介绍下载模型的方法&#xff0c;适合新手入…

win | wireshark | 在win上跑lua脚本 解析数据包

前提说明&#xff1a;之前是在linux 系统上配置的&#xff0c;然后现在 在配置lua 脚本 &#xff0c;然后 分析指定协议 的 数据包 其实流程也比较简单&#xff0c;但 逻辑需要缕清来 首先要把你 预先准备的 xxx.lua 文件放到wireshark 的安装文件中&#xff0c;&#xff08;我…

deque容器

1 deque容器基本概念 功能&#xff1a; 双端数组&#xff0c;可以对头端进行插入删除操作 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低deque相对而言&#xff0c;对头部的插入删除速度回比vector快vector访问…

python实现MQTT协议(发布者,订阅者,topic)

python实现MQTT协议 一、简介 1.1 概述 本文章针对物联网MQTT协议完成python实现 1.2 环境 Apache-apollo创建brokerPython实现发布者和订阅者 1.3 内容 MQTT协议架构说明 &#xff1a; 利用仿真服务体会 MQTT协议 针对MQTT协议进行测试 任务1&#xff1a;MQTT协议应…

maven搭建spring项目

前提 安装jdk 安装maven 安装eclipse 创建maven项目 搭建spring项目 pom.xml <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.4.RELEASE</version> </dependency&…

【java 入侵 C# 之路】1-入门

感谢 https://www.cnblogs.com/mww-NOTCOPY/p/12213373.html 百度百科 jvm对应clr java se runtime对应 .net framework&#xff0c; jdk对应 .net framework sdk&#xff0c; java对应C# .NET 是开发者平台&#xff0c;它包含开发环境、技术框架、社区论坛、服务支持等&…

学习pytorch8 土堆说卷积操作

土堆说卷积操作 官网debug torch版本只有nn 没有nn.functional代码执行结果 B站小土堆视频学习笔记 官网 https://pytorch.org/docs/stable/nn.html#convolution-layers 常用torch.nn, nn是对nn.functional的封装&#xff0c;使函数更易用。 卷积核从输入图像左上角&#xf…

Bito使用手册

第一步&#xff1a;输入网站 https://alpha.bito.co/bitoai/ 第二步&#xff1a;填写邮箱 第三步&#xff1a;登录邮箱&#xff0c;获取验证码 第四步&#xff1a;填写验证码 第五步&#xff1a;完成