• 设计模式之建造者模式(七)


     一、引出模式

    开发场景:在前面工厂方法模式中我们提到出将数据以XML、TXT和数据库形式导出。现在我们再深化一下,对导出数据的格式进行约束。

    导出的文件,不管是格式,都分为文件头、文件体和文件尾

    文件头部分,需要描述:分公司名称、数据导出时间,

    文件体部分,需要描述:表的名称,单独一行。还有一些数据描述。

    文件尾部分,需要描述:导出人。 

    当我们不使用模式时,不管是输出输出成文本文件还是,输出到XML,步骤基本上是不变的,都要经过以下四步

    1)  拼接文件头内容

    2)  拼接文件体内容

    3)  拼接文件尾内容

    4)  将内容输出到相应格式 

    现在就是存在在使用输出格式时,都会重复这几个处理步骤,我们应该将其提炼出来,形成公共的处理过程,还要保证在处理过程不变的情况下,能方便的切换不同的输出格式。

    二、认识模式

    1.模式定义

    将一个复杂对象的构建与它的表示相分离,使得同样的构建过程可以创建不同的表示。

     在上述案例中,四步步骤就是构建过程,不同导出方式就是表示。

    2.解决思路

    要实现同样的构建过程可以创建不同的表现,第一件事就是将构建过程给独立出来,在建造者模式中把它称为指导者,由它来指导装配的过程,但不负责每步的具体实现。当然,光有指导者是不够的,必须要有能实现每步的对象,在建造者模式中称这些对象为建造器。

    这样,指导者就是以重用的构建过程,而建造器是可以被切换的具体实现。

    3.模式原型

     

    Builder:建造器接口,定义创建一个Product对象所需要的各个部件的操作。

    ConcreteBuilder:具体的建造器实现。实现各个部件的创建,并负责组将Product对象的各个部件,同时还提供一个让用户获取组将完成后的产品对象的方法。

    Dorector:指导者,主要用来使用Builder接口,以一个统一程来构建所需要的Product对象。

    Product:产品,表示被建造器构建的复杂对象,包含多个部件。

    示例代码

        class Program
        {
            static void Main(string[] args)
            {
                Director director = new Director(new ConcreteBuilder());
                director.ConStruct();
     
                Console.Read();
            }
        }
     
        public class Director
        {
            //指导者需要持有建造器的引用
            private Builder builders;
     
            //指定具体的建造器
            public Director(Builder builder)
            {
                this.builders = builder;
            }
     
            //间接调用建造器的方法
            public void ConStruct()
            {
                builders.BuildPart();
            }
     
        }
     
        /// <summary>
        /// 建造器
        /// </summary>
        public abstract class Builder
        {
           public abstract void BuildPart();
        }
     
        /// <summary>
        /// 具体的建造器
        /// </summary>
        public class ConcreteBuilder : Builder
        {
            //建造器需要持有产品的引用
            private Product resultProduct;
     
            public override void BuildPart()
            {
                Console.WriteLine("BuilderPartA");
            }
     
            /// <summary>
            /// 返回产品
            /// </summary>
            /// <returns></returns>
            public Product GetResult()
            {
                return resultProduct;
            }
        }
     
        /// <summary>
        /// 产品
        /// </summary>
        public class Product
        {
     
        }

    4.上述案例的模式结构

    示例代码:

            class Program
        {
            static void Main(string[] args)
            {
                //准备测试数据
                ExportHeaderModel ehm = new ExportHeaderModel();
                ehm.DepId = "一分公司";
                ehm.ExportDate = "2010-05-18";
     
                Dictionary<String, List<ExportDataModel>> dicData = new Dictionary<String, 
    
    List<ExportDataModel>>();
     
                List<ExportDataModel> list = new List<ExportDataModel>();
     
                ExportDataModel edm1 = new ExportDataModel();
                edm1.ProductId = "产品001号";
                edm1.Price = 100;
                edm1.Amount = 80;
     
                ExportDataModel edm2 = new ExportDataModel();
                edm2.ProductId = "产品002号";
                edm2.Price = 99;
                edm2.Amount = 55;
     
                //把数据组装起来
                list.Add(edm1);
                list.Add(edm2);
     
                dicData.Add("销售记录表", list);
     
                ExportFooterModel efm = new ExportFooterModel();
                efm.ExportUser = "张三";
     
                //测试输出到文本文件
                TxtBuilder txtBuilder = new TxtBuilder();
                //创建指导者对象
                Director director = new Director(txtBuilder);
                director.Construct(ehm, dicData, efm);
                Console.WriteLine("输出到文本文件的内容:
    " + txtBuilder.GetResult());
     
                Console.WriteLine("--------------------------------------------------------");
     
                //测试输出到xml文件
                XmlBuilder xmlBuilder = new XmlBuilder();
     
                Director director2 = new Director(xmlBuilder);
                director2.Construct(ehm, dicData, efm);
     
                //xmlBuilder.BuildHeader(ehm);
                //xmlBuilder.BuildBody(dicData);
                //xmlBuilder.BuildFooter(efm);
     
                //把要输出的内容输出到控制台看看
                Console.WriteLine("输出到XML文件的内容:
    " + xmlBuilder.GetResult());
     
                Console.Read();
            }
        }
     
        #region 指导者
     
        /// <summary>
        /// 指导者,指导使用构建器的接口来构建输出的文件的对象
        /// </summary>
        public class Director
        {
     
            /// <summary>
            /// 持有当前需要使用的构建器对象
            /// </summary>
            private Builder builder;
     
            /// <summary>
            /// 构造方法,传入构建器对象
            /// </summary>
            /// <param name="builder"></param>
            public Director(Builder builder)
            {
                this.builder = builder;
            }
     
            /// <summary>
            /// 指导构建器构建最终的输出的文件的对象
            /// </summary>
            /// <param name="ehm">文件头的内容</param>
            /// <param name="dicData">数据的内容</param>
            /// <param name="efm">文件尾的内容</param>
            public void Construct(ExportHeaderModel ehm, Dictionary<String, List<ExportDataModel>> dicData, 
    
    ExportFooterModel efm)
            {
                //可以在这进行校验
     
                //1:先构建Header
                builder.BuildHeader(ehm);
     
                //实现一个Header的后处理
     
                //可以在这里实现一些业务
     
                //2:然后构建Body
                builder.BuildBody(dicData);
     
                //3:然后构建Footer
                builder.BuildFooter(efm);
            }
        }
        #endregion
     
        #region Builder接口
     
        /// <summary>
        /// 构建器接口,定义创建一个输出文件对象所需的各个部件的操作
        /// </summary>
        public interface Builder
        {
            /// <summary>
            /// 构建输出文件的Header部分
            /// </summary>
            /// <param name="ehm"></param>
            void BuildHeader(ExportHeaderModel ehm);
     
            /// <summary>
            /// 构建输出文件的Body部分
            /// </summary>
            /// <param name="mapData"></param>
            void BuildBody(Dictionary<string, List<ExportDataModel>> dicData);
     
            /// <summary>
            /// 构建输出文件的Footer部分
            /// </summary>
            /// <param name="efm"></param>
            void BuildFooter(ExportFooterModel efm);
        }
        #endregion
     
        #region ConcreteBuilder 导出到Txt
     
        /// <summary>
        /// 实现导出数据到文本文件的的构建器对象
        /// </summary>
        public class TxtBuilder : Builder
        {
            /// <summary>
            /// 用来记录构建的文件的内容,相当于产品
            /// </summary>
            private StringBuilder strBuilder = new StringBuilder();
            public void BuildBody(Dictionary<string, List<ExportDataModel>> dicData)
            {
                foreach (var tblName in dicData.Keys)
                {
                    strBuilder.Append(tblName + "
    ");
                    foreach (ExportDataModel edm in dicData.Values.FirstOrDefault())
                    {
                        strBuilder.Append(edm.ProductId + "," + edm.Price + "," + edm.Amount);
                    }
                }
            }
     
            public void BuildFooter(ExportFooterModel efm)
            {
                strBuilder.Append(efm.ExportUser);
            }
     
            public void BuildHeader(ExportHeaderModel ehm)
            {
                strBuilder.Append(ehm.DepId + "," + ehm.ExportDate + "
    ");
            }
     
            public string GetResult()
            {
                return strBuilder.ToString();
            }
        }
        #endregion
     
        #region ConcreteBuilder 导出到XML文件
     
        /// <summary>
        /// 实现导出数据到XML文件的的构建器对象
        /// </summary>
        public class XmlBuilder : Builder
        {
            /// <summary>
            /// 用来记录构建的文件的内容,相当于产品
            /// </summary>
            private StringBuilder strBuilder = new StringBuilder();
     
            public void BuildHeader(ExportHeaderModel ehm)
            {
                strBuilder.Append("<?xml version='1.0' encoding='gb2312'?>
    ");
                strBuilder.Append("<Report>
    ");
                strBuilder.Append("  <Header>
    ");
                strBuilder.Append("    <DepId>" + ehm.DepId + "</DepId>
    ");
                strBuilder.Append("    <ExportDate>" + ehm.ExportDate + "</ExportDate>
    ");
                strBuilder.Append("  </Header>
    ");
            }
     
            public void BuildBody(Dictionary<string, List<ExportDataModel>> dicData)
            {
                strBuilder.Append("  <Body>
    ");
                foreach (var tblName in dicData.Keys)
                {
                    strBuilder.Append("    <Datas TableName="" + tblName + "">
    ");
                    foreach (ExportDataModel edm in dicData.Values.FirstOrDefault())
                    {
                        strBuilder.Append("      <Data>
    ");
                        strBuilder.Append("        <ProductId>" + edm.ProductId + 
    
    "</ProductId>
    ");
                        strBuilder.Append("        <Price>" + edm.Price + "</Price>
    ");
                        strBuilder.Append("        <Amount>" + edm.Amount + "</Amount>
    ");
                        strBuilder.Append("      </Data>
    ");
                    }
                    strBuilder.Append("    </Datas>
    ");
                }
                strBuilder.Append("  </Body>
    ");
            }
     
            public void BuildFooter(ExportFooterModel efm)
            {
                //对象的创建过程
     
                //不是由自己来创建对象,而是使用其它组件创建的对象
                //比如:简单工厂、工厂方法
                MyFooter mf = FooterFactory.createMyFooter();
     
                //组件组装过程
                strBuilder.Append(mf.GenHeader(efm));
            }
     
     
            public string GetResult()
            {
                return strBuilder.ToString();
            }
     
        }
     
        /// <summary>
        /// XML 文件尾工厂
        /// </summary>
        public class FooterFactory
        {
            public static MyFooter createMyFooter()
            {
                return new MyFooter();
            }
        }
     
        /// <summary>
        /// XML 具体实现
        /// </summary>
        public class MyFooter
        {
            public String GenHeader(ExportFooterModel efm)
            {
                String str = "  <Footer>
    ";
                str += "    <ExportUser>" + efm.ExportUser + "</ExportUser>
    ";
                str += "  </Footer>
    ";
                str += "</Report>
    ";
                return str;
            }
        }
        #endregion
     
        #region 产品
     
        /// <summary>
        /// 文件头部分
        /// </summary>
        public class ExportHeaderModel
        {
            /// <summary>
            /// 分公司或门市点编号
            /// </summary>
            public String DepId { get; set; }
            /// <summary>
            /// 导出数据的日期
            /// </summary>
            public String ExportDate { get; set; }
        }
     
        /// <summary>
        /// 文件体部分
        /// </summary>
        public class ExportDataModel
        {
            /// <summary>
            /// 产品编号
            /// </summary>
            public string ProductId { get; set; }
            /// <summary>
            /// 产品价格
            /// </summary>
            public int Price { get; set; }
            /// <summary>
            /// 产品数量
            /// </summary>
            public int Amount { get; set; }
        }
     
        /// <summary>
        /// 文件尾部分
        /// </summary>
        public class ExportFooterModel
        {
            /// <summary>
            /// 输出人
            /// </summary>
            public String ExportUser { get; set; }
        }
        #endregion

    三、理解模式

    1.认识模式

    按照封装变化的原理,建造者模式实则是封装对象创建的变化,主要是指对象内部构建的创建。建造者模式的主要功能就是构建复杂的产品,这个构建的过程是统一的,固定不变的,变化的部分放到建造器部分,只要配置不同的建造器,同样的构建过程,就能出来不同的产品。

    建造者模式的重心在于分离构建算法和具体的构造实现。

    2.建造者模式的构成

    建造者模式分为两个很重要的部分。

    1)  一个是Builder接口,这里定义了如何构建各个部件,也就是知道每个部件功能如何实现。

    2)  另一部分就是Director,指导者是知道如何组合来构建产品,也就是负责整体的算法。

     不论怎么变化,建造者模式都是存在这两个部分的,一部分是部件构造和产品装配,另一部分是整体构建的算法。注意,这里是说部分,而不是一定要存在指导者或者Builder接口这两个类。

     3.关于被构建的产品的接口

    在使用建造者模式时,大多数情况下是不知道最终构建出来的产品是怎么样的,所以在标准的建造者模式中,一般是不需要给产品定义接口的。

     4.建造者模式的演化

    建造者模式在使用的过程中可以演化出多种形式。

    1)省略抽象建造者角色

    如果系统中只需要一个具体建造者的话,可以省略掉抽象建造者。

    2)省略指导者角色

    在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略掉,那么还可以省略掉指导者角色。让Builder角色自己扮演指导者与建造者双重角色。

    只要牢牢把握,建造者模式的两个主要部分(第2点提到)就可以了。

    5.建造者模式的优点

    1)  松散耦合

    2)  可以很容易地改变产品内部表示

    3)  更好的复用性

    6.何时选用建造者模式

    1).如果创建对象的算法,应该独立于该对象的组成不封、 以及它们的装配方式时,使用建造者模式。

    2).如果同一个构建过程有着不同的表示时,使用建造者模式。

     7.相关模式

    建造者模式和工产方法模式

    这两个模式可以组合使用。

    工厂方法模式与建造者模式并没有泾渭分明的区别,尤其针对建造者模式,可以将其转化为工厂方法模式,反之则不然。也就是说工厂方法模式应用更加广泛。如果一定要做区分,二者都是生产产品的,工厂方法模式主要用于生成一类完整的产品,而建造者模式则更关注对产品内部的创建进行组合,并最终组装为整体的产品。

    建造者模式与抽象工厂

    这两个模式可以组合使用。

    在建造者模式实现中,需要创建各个部件对象,而这些部件对象是有关联的,通常是构成一个复杂对象的部件对象。也就是说,建造者模式实现中,需要获取构成一个复杂对象的产品簇,那么自然就可以使用抽象工厂模式来实现了,这样,抽象工厂负责部件对象的创建,建造者模式实现里则主要负责产品对象的整体构建。

    8.小结

    建造者模式重心还在于分离整体构建算法和部件构造。分步骤构建对象不过是整体构建算法的一个简单表现,或者说是一个附带的产物。

  • 相关阅读:
    数据结构与算法-基础(七)完全二叉树
    数据结构与算法-基础(六)二叉树基础
    数据结构与算法-基础(五)队列(Qeque)
    数据结构与算法-基础(四)栈(Stack)
    数据结构与算法-基础(三)- 循环链表(补充)
    数据结构与算法-基础(二)单向链表
    数据结构与算法-基础(一)动态数组
    Swift-Button 的 highlighted(高亮)
    Android现有工程使用Compose
    Android Jetpack Compose 引入示例工程
  • 原文地址:https://www.cnblogs.com/zxj159/p/3431498.html
Copyright © 2020-2023  润新知