博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式之建造者模式(七)
阅读量:6688 次
发布时间:2019-06-25

本文共 8914 字,大约阅读时间需要 29 分钟。

 一、引出模式

开发场景:在前面工厂方法模式中我们提到出将数据以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();        }     }     ///     /// 建造器    ///     public abstract class Builder    {       public abstract void BuildPart();    }     ///     /// 具体的建造器    ///     public class ConcreteBuilder : Builder    {        //建造器需要持有产品的引用        private Product resultProduct;         public override void BuildPart()        {            Console.WriteLine("BuilderPartA");        }         ///         /// 返回产品        ///         /// 
public Product GetResult() { return resultProduct; } } /// /// 产品 /// public class Product { }

4.上述案例的模式结构

示例代码:

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

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

转载地址:http://vezoo.baihongyu.com/

你可能感兴趣的文章
Java反射总结
查看>>
为什么要使用SLF4J而不是Log4J
查看>>
day4 二维数组旋转90度
查看>>
简说设计模式——组合模式
查看>>
第二次实训作业
查看>>
Java 9.0.4版本 包裹类型和基本类型 ==和equals的比较
查看>>
数组多重筛选条件排序方法
查看>>
Vue中import引入模块路径时的@符号
查看>>
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
查看>>
sublime text 3插件
查看>>
Javascript优化后的加减乘除(解决js浮点数计算bug)
查看>>
js中的super小结
查看>>
ios显示或隐藏导航栏的底线
查看>>
包含 min 函数的栈
查看>>
rm -f /var/lib/rpm/__db*;rpm --rebuilddb
查看>>
iOS进公司后可能用到的开源库和第三方组件
查看>>
一篇文章,带你了解gulp
查看>>
前端基础知识复习之CSS
查看>>
命令模式与它在源码中的运用
查看>>
再和“面向对象”谈恋爱—面向对象编程概念
查看>>