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

 一、引出模式

开发场景:在前面工厂方法模式中我们提到出将数据以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("输出到文本文件的内容:\n" + 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文件的内容:\n" + 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 + "\n");
                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 + "\n");
        }
 
        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'?>\n");
            strBuilder.Append("<Report>\n");
            strBuilder.Append("  <Header>\n");
            strBuilder.Append("    <DepId>" + ehm.DepId + "</DepId>\n");
            strBuilder.Append("    <ExportDate>" + ehm.ExportDate + "</ExportDate>\n");
            strBuilder.Append("  </Header>\n");
        }
 
        public void BuildBody(Dictionary<string, List<ExportDataModel>> dicData)
        {
            strBuilder.Append("  <Body>\n");
            foreach (var tblName in dicData.Keys)
            {
                strBuilder.Append("    <Datas TableName=\"" + tblName + "\">\n");
                foreach (ExportDataModel edm in dicData.Values.FirstOrDefault())
                {
                    strBuilder.Append("      <Data>\n");
                    strBuilder.Append("        <ProductId>" + edm.ProductId + 

"</ProductId>\n");
                    strBuilder.Append("        <Price>" + edm.Price + "</Price>\n");
                    strBuilder.Append("        <Amount>" + edm.Amount + "</Amount>\n");
                    strBuilder.Append("      </Data>\n");
                }
                strBuilder.Append("    </Datas>\n");
            }
            strBuilder.Append("  </Body>\n");
        }
 
        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>\n";
            str += "    <ExportUser>" + efm.ExportUser + "</ExportUser>\n";
            str += "  </Footer>\n";
            str += "</Report>\n";
            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.小结

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

posted @ 2013-11-19 15:05  烧点饭  阅读(896)  评论(0编辑  收藏  举报