设计模式之(五)建造者(生成器)模式(Builder)
考虑这样一种业务场景,我们构建一个业务对象,但是这个业务对象及其复杂。为了代码的根号的可读性,我们会把这个对象的构建过程根据精密联系的程度来拆分成几个类来完成。最后再放到一起使用来生成复杂对象。这个业务场景非常常见,接下来就分析一下解决这个问题更灵活的方式,即:建造者模式。
建造者模式定义及规范代码
将一个复杂对象的创建与它的表示分离,使得同样的创建过程可以创建不同的表示。
看到这个定义以后,肯定有很多读者会有疑问,【复杂对象的创建】的创建好理解,但是【它(复杂对象)的表示】是什么意思呢,这里为了解决读者可能出现猴急的心情,先大概解释一下。首先看建造者模式的结构图
Builder:生成器接口,定义创建一个产品所需要的的各个部分的操作。
ConcreateBuilder:具体的生成器实现,这个产品在组装过程中各个部分所需要的共同的操作。并且还提供一个供用户获取组装完产品的对象。
Director:指导者,主要用来使用 builder 接口,以一个统一的过程来构建所需要的 Product 对象
Product:产品,表示被生成器构建的复杂对象,包含多个部件。
接下来说下我的理解。建造者模式在创建相似的复杂对象使用,其中 Builder 定义创建这个复杂对象的过程,把创建过程分为不同的部分,也可以说是不同的组成部分。而 ConcreateBuilder 则是对复杂对象创建每部分共同的部分实现出来。
相似复杂对象的每步骤相似部分在 ConcreateBuilder 创建,而不同部分则另外创建,以参数形式传入相应的部分。这个具体如何组装,则是在 Director 中来完成。在这个初步理解的基础上。通过以下例子来具体分析。
1 /** 2 * 生成器接口,定义创建一个输出文件对象所需的各个部件的操作 3 * @author Administrator 4 */ 5 public interface Builder { 6 7 //构建输出的文件头内容 8 public void builderHeader(ExportHeaderModel ehm); 9 10 //构建输出的文件正文数据内容 11 public void BuilderBody(Map<String,Collection<ExportDataModel>> bodyData); 12 13 //构建文件尾内容 14 public void builderFooter(ExportFooterModel efm); 15 } 16 17 /** 18 * 描述输出到文件头部内容的对象 19 */ 20 public class ExportHeaderModel { 21 22 /** 23 * 分公司或门市店的编号 24 */ 25 private String DepID; 26 27 /** 28 * 导出数据的日期 29 */ 30 private String exportDate; 31 32 public String getDepID() { 33 return DepID; 34 } 35 36 public void setDepID(String depID) { 37 DepID = depID; 38 } 39 40 public String getExportDate() { 41 return exportDate; 42 } 43 44 public void setExportDate(String exportDate) { 45 this.exportDate = exportDate; 46 } 47 } 48 49 /** 50 * 描述生成文件输的对象 51 * @author Administrator 52 * 53 */ 54 public class ExportDataModel { 55 56 /** 57 * 成品编号 58 */ 59 private String productID; 60 61 /** 62 * 产品价格 63 */ 64 private double price; 65 66 /** 67 * 销售数量 68 */ 69 private double amount; 70 71 public String getProductID() { 72 return productID; 73 } 74 75 public void setProductID(String productID) { 76 this.productID = productID; 77 } 78 79 public double getPrice() { 80 return price; 81 } 82 83 public void setPrice(double price) { 84 this.price = price; 85 } 86 87 public double getAmount() { 88 return amount; 89 } 90 91 public void setAmount(double amount) { 92 this.amount = amount; 93 } 94 } 95 96 97 /** 98 * 描述输出到文件尾内容的对象 99 * @author Administrator 100 * 101 */ 102 public class ExportFooterModel { 103 104 /** 105 * 输出人 106 */ 107 private String exportUser; 108 109 public String getExportUser() { 110 return exportUser; 111 } 112 113 public void setExportUser(String exportUser) { 114 this.exportUser = exportUser; 115 } 116 } 117 118 /** 119 * 相当于 ConcreteBuilder 120 * @author Administrator 121 * 122 */ 123 public class TxtBuilder implements Builder { 124 125 //用来构建文件的内容,相当于产品 126 private StringBuffer buffer = new StringBuffer(); 127 128 @Override 129 public void builderHeader(ExportHeaderModel ehm) { 130 // TODO Auto-generated method stub 131 buffer.append("分公司编号:"+ehm.getDepID()+",导入日期:"+ehm.getExportDate()+"\n"); 132 } 133 134 @Override 135 public void BuilderBody(Map<String, Collection<ExportDataModel>> bodyData) { 136 // TODO Auto-generated method stub 137 for(String tblName:bodyData.keySet()){ 138 buffer.append(tblName+"\n"); 139 for(ExportDataModel edm:bodyData.get(tblName)){ 140 buffer.append(edm.getProductID()+", "+edm.getPrice()+", "+edm.getAmount()+"\n"); 141 } 142 } 143 } 144 145 @Override 146 public void builderFooter(ExportFooterModel efm) { 147 // TODO Auto-generated method stub 148 buffer.append(efm.getExportUser()+"\n"); 149 } 150 151 public StringBuffer getResult(){ 152 return buffer; 153 } 154 } 155 156 157 /** 158 * 相当于 ConcreteBuilder 159 * @author Administrator 160 * 161 */ 162 public class XmlBuilder implements Builder { 163 164 //用来构建文件的内容,相当于产品 165 private StringBuffer buffer = new StringBuffer(); 166 167 @Override 168 public void builderHeader(ExportHeaderModel ehm) { 169 // TODO Auto-generated method stub 170 buffer.append("<?xml verson='1.0' encoding='gb2312'?>\n"); 171 buffer.append("<Report>\n"); 172 buffer.append(" <Header>\n"); 173 buffer.append(" <DeptID>"+ehm.getDepID()+"</DeptID>\n"); 174 buffer.append(" <ExportDate>"+ehm.getExportDate()+"</ExportDate>\n"); 175 buffer.append(" </Header>\n"); 176 } 177 178 @Override 179 public void BuilderBody(Map<String, Collection<ExportDataModel>> bodyData) { 180 // TODO Auto-generated method stub 181 buffer.append(" <Body>\n"); 182 for(String tblname:bodyData.keySet()){ 183 buffer.append(" <Dates TableName = \""+tblname+"\">\n"); 184 for(ExportDataModel edm:bodyData.get(tblname)){ 185 buffer.append(" <Date>\n"); 186 buffer.append(" <ProductID> "+edm.getProductID()+"</ProductID>\n"); 187 buffer.append(" <Price> "+edm.getPrice()+"</Price>\n"); 188 buffer.append(" <Amount> "+edm.getAmount()+"</Amount>\n"); 189 } 190 } 191 buffer.append(" <Body>\n"); 192 } 193 194 @Override 195 public void builderFooter(ExportFooterModel efm) { 196 // TODO Auto-generated method stub 197 buffer.append(" <Footer>\n"); 198 buffer.append(" <User>"+efm.getExportUser()+"</User>\n"); 199 buffer.append(" </Footer>\n"); 200 buffer.append("</Report>\n"); 201 } 202 203 public StringBuffer getResult(){ 204 return buffer; 205 } 206 207 } 208 209 210 public class Director { 211 212 private Builder builder; 213 214 public Director(Builder builder){ 215 this.builder = builder; 216 } 217 218 public void construct(ExportHeaderModel ehm,Map<String,Collection<ExportDataModel>> bodyData,ExportFooterModel efm){ 219 builder.builderHeader(ehm); 220 221 builder.BuilderBody(bodyData); 222 223 builder.builderFooter(efm); 224 } 225 } 226 227 228 public class Client { 229 230 public static void main(String[] args) { 231 ExportHeaderModel ehm = new ExportHeaderModel(); 232 ehm.setDepID("0001"); 233 ehm.setExportDate("2019-04-26"); 234 235 ExportDataModel edm = new ExportDataModel(); 236 edm.setProductID("cp001"); 237 edm.setPrice(5.56); 238 edm.setAmount(133333); 239 240 ExportDataModel edm2 = new ExportDataModel(); 241 edm2.setProductID("cp001"); 242 edm2.setPrice(5.56); 243 edm2.setAmount(133333); 244 245 Map<String,Collection<ExportDataModel>> data = new HashMap<String,Collection<ExportDataModel>>(); 246 Collection<ExportDataModel> collect = new ArrayList<ExportDataModel>(); 247 248 collect.add(edm); 249 collect.add(edm2); 250 251 data.put("bodys", collect); 252 253 ExportFooterModel efm = new ExportFooterModel(); 254 efm.setExportUser("pwg"); 255 256 TxtBuilder tb = new TxtBuilder(); 257 Director dr = new Director(tb); 258 259 dr.construct(ehm, data, efm); 260 261 System.out.println("输出到文本文件:"+tb.getResult()); 262 263 XmlBuilder xml = new XmlBuilder(); 264 Director dr2 = new Director(xml); 265 dr2.construct(ehm, data, efm); 266 267 System.out.println("输出到 xml 文件:\n"+xml.getResult()); 268 } 269 270 271 }
上面的代码例子展示了建造者模式的功能。其中为了比较好的展示实现了两个不同的产品,实现了两个不同的表示,而 Director 只是实现了一个,但是通过例子可以看出,如果需要。也可以实现不同 Director ,即产品的组装过程。这样产品就是很灵活的扩展性,并且产品的表示和组建过程解耦。
进而分析出建造者模式的本事是区分 复杂对象的创建 和 它的表示 。这两个在代码中的体现是 Director 和 XmlBuilder TxtBuilder。
分析建造者\生成器模式
建造者模式应用场景:可以灵活的、易扩展的创建复杂对象。而解决的办法就是分离构建算法和具体构造实现。这样在扩展的时候可以根据需要切换 构建算法(Director )和 具体构造实现 (Builder)。只要抓住了这点,基本就理解了生成器的本质了。