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

news2025/1/18 11:54:22

一、上篇回顾

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

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

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

上篇中,有不少的朋友提出了一些意见和建议,首先很感谢大伙的支持和鼓励,有朋友提出来,我画的图不够专业,专业人士应该用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.coloradmin.cn/o/967360.html

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

相关文章

2.5 关系查询优化

这段话主要讨论了关系模型在数据库领域中的查询优化问题。以下是对这段文字的简要解释&#xff1a; 1. **关系模型的优缺点**&#xff1a;虽然关系模型有许多优点&#xff0c;但它也有一些缺点&#xff0c;最主要的缺点是查询效率。如果没有适当的优化&#xff0c;查询的速度可…

git在linux情况下设置git 命令高亮

只需要执行下面这个命令&#xff0c;这样就可以在查看git status明亮的时候高亮显示。 git config --global color.status auto未设置前 谁知之后

【leetcode 力扣刷题】字符串匹配之经典的KMP!!!

字符串子串匹配相关 28. 找出字符串中第一个匹配项的下标暴力求解KMP 459. 重复的子字符串暴力求解在SS中找S 以下是能用KMP求解的算法题&#xff0c;KMP是用于字符串匹配的经典算法【至今没学懂………啊啊啊】 28. 找出字符串中第一个匹配项的下标 题目链接&#xff1a;28. 找…

Vue学习(三)

一、列表渲染 v-for指令 用于展示列表数据 语法<li v-for"(item, index) in items" :key"index"></li>key可以是index,最好是遍历对象的唯一标识 可遍历&#xff1a;数组、对象 <!DOCTYPE html> <html lang"en">&l…

探究Vue3中的Composition API:优化组件逻辑的新利器

一、toRef函数 在 Vue 3.0 中&#xff0c;引入了一种新的响应式 API,即 toRef。toRef 函数可以将一个普通值转换为响应式引用类型&#xff0c;这样就可以在模板中直接使用这个响应式引用类型的属性&#xff0c;并且当该属性发生变化时&#xff0c;视图会自动更新。 <templat…

【C++代码】用栈实现队列,用队列实现栈--代码随想录

队列是先进先出&#xff0c;栈是先进后出。卡哥给了关于C方向关于栈和队列的4个问题&#xff1a; C中stack 是容器么&#xff1f; 使用的stack是属于哪个版本的STL&#xff1f; 使用的STL中stack是如何实现的&#xff1f; stack 提供迭代器来遍历stack空间么&#xff1f; …

测试开发【Mock平台】09开发:项目管理(五)搜索、删除和Table优化

【Mock平台】为系列测试开发教程&#xff0c;从0到1编码带你一步步使用Spring Boot 和 Antd React框架完成搭建一个测试工具平台&#xff0c;希望作为一个实战项目对各位的测试开发学习之路有帮助&#xff0c;大奇一个专注测试技术干货原创与分享的家伙。 Mock平台系统项目基本…

【大数据实训】基于Hive的北京市天气系统分析报告(二)

博主介绍&#xff1a;✌全网粉丝6W,csdn特邀作者、博客专家、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于大数据技术领域和毕业项目实战✌ &#x1f345;文末获取项目联系&#x1f345; 目录 1. 引言 1.1 项目背景 1 1.2 项目意义 1 2.…

蓝队追踪者工具TrackAttacker,以及免杀马生成工具

蓝队追踪者工具TrackAttacker&#xff0c;以及免杀马生成工具。 做过防守的都知道大HW时的攻击IP量&#xff0c;那么对于这些攻击IP若一个个去溯源则显得效率低下&#xff0c;如果有个工具可以对这些IP做批量初筛是不是更好&#xff1f; 0x2 TrackAttacker获取 https://githu…

[管理与领导-67]:IT基层管理者 - 辅助技能 - 4- 职业发展规划 - 评估你与公司的八字是否相合

目录 前言&#xff1a; 一、概述 二、八字相合的步骤 2.1 企业文化是否相合 2.2.1 企业文化对职业选择的意义 2.2.2 个人与企业三观不合的结果 2.2.3 什么样的企业文化的公司不能加入 2.2 公司的发展前景 2.3 公司所处行业发展 2.4 创始人的三观 2.5 创始人与上司的…

HDMI 输出实验

FPGA教程学习 第十四章 HDMI 输出实验 文章目录 FPGA教程学习前言实验原理实验过程程序设计时钟模块&#xff08;video_pll&#xff09;彩条产生模块&#xff08;color_bar)配置数据查找表模块&#xff08;lut_adv7511&#xff09;I2C Master 寄存器配置模块&#xff08;i2c_c…

692. 前K个高频单词

题目来源&#xff1a;力扣 题目描述&#xff1a; 给定一个单词列表 words 和一个整数 k &#xff0c;返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率&#xff0c; 按字典顺序 排序。 示例 1&#xff1a; 输入:…

css transition 指南

css transition 指南 在本文中&#xff0c;我们将深入了解 CSS transition&#xff0c;以及如何使用它们来创建丰富、精美的动画。 基本原理 我们创建动画时通常需要一些动画相关的 CSS。 下面是一个按钮在悬停时移动但没有动画的示例&#xff1a; <button class"…

【斗罗Ⅱ】最强武魂揭秘,98级玄老、95级言少哲神兽级武魂曝光

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析【绝世唐门】 在斗罗大陆动画绝世唐门中&#xff0c;98级玄老已经登场&#xff0c;他是一个很随意的老人&#xff0c;乍眼一看&#xff0c;似乎是一个邋里邋遢、好吃懒做的人&#xff0c;但是实际上他却是史莱克学院重量级…

精心整理了优秀的GitHub开源项目,包含前端、后端、AI人工智能、游戏、黑客工具、网络工具、AI医疗等等,空闲的时候方便看看提高自己的视野

精心整理了优秀的GitHub开源项目&#xff0c;包含前端、后端、AI人工智能、游戏、黑客工具、网络工具、AI医疗等等&#xff0c;空闲的时候方便看看提高自己的视野。 刚开源就变成新星的 igl&#xff0c;不仅获得了 2k star&#xff0c;也能提高你开发游戏的效率&#xff0c;摆…

自建音乐服务器Navidrome之二

6 准备音乐资源 可选 Last.fm Lastfm是 Audioscrobbler 音乐引擎设计团队的旗舰产品&#xff0c;以英国为总部的网络电台和音乐社区。有遍布232个国家超过1500万的活跃听众。据说有6亿音乐资源。 docker-compose.yml 配置 Navidrome 可以从 Last.fm 和 Spotify 获取专辑信息和…

性能测试 —— Jmeter 命令行详细

我们在启动Jmeter时 会看见&#xff1a;Don’t use GUI mode for load testing !, only for Test creation and Test debugging.For load testing, use CLI Mode (was NON GUI) 这句话的意思就是说&#xff0c;不要使用gui模式进行负载测试&#xff0c;gui模式仅仅是创建脚本…

涂鸦智能携手亚马逊云科技 共建“联合安全实验室” 为IoT发展护航

2023年8月31日&#xff0c;全球化IoT开发者平台涂鸦智能&#xff08;NYSE: TUYA&#xff0c;HKEX: 2391&#xff09;在“2023亚马逊云科技re:Inforce中国站”大会宣布与全球领先的云计算公司亚马逊云科技共同成立“联合安全实验室”&#xff0c;旨在加强IoT行业的安全合规能力与…

管理类联考——逻辑——形式逻辑——汇总篇——知识点突破——论证逻辑——假设——否定代入

角度——原理 可以用“否定代入法”验证疑似选项 由于假设是使推理成立的一个必要条件&#xff0c;根据必要条件的性质若Р是S的必要条件&#xff0c;那么┐P → ┐S可知&#xff0c;如果一个推理在没有某一条件时&#xff0c;这个推理必然不成立&#xff0c;那么这个条件就是…

41.岛屿数量(第四期模拟笔试)(BFS练习题)

题目&#xff1a; 给定一个 m 行 n 列的二维地图&#xff0c;初始化每个单元格都是海洋&#xff0c;二维地图外也全是海洋。 操作 addLand 会将单元格&#xff08;col, row&#xff09;变为陆地。 定义一系列相连的被海洋包围的陆地为岛屿&#xff0c; 横向相邻或者纵向相连的…