前面讲述了工厂模式,主要用于创建对象;建造者模式与工厂模式的相同之处在于都属于创建型的设计模式,即都是为解决方便创建对象而产生的设计模式!
不同之处在于,工厂模式一般用于创建产品本身较为简单,但是产品种类比较多,产品分类较复杂的场景;建造者模式一般用于创建产品本身比较复杂,但是不同产品的创建都需要遵循一定的流程章法,并且产品在组成上不存在很大的相似性,而展现有较大差异。
建造者模式的思想
产品 - 可以是抽象类,也可以是普通类
抽象建造者 - 约束建造所需流程。抽象类
实际建造者(继承与抽象建造者) - 实际生产产品的类
导演类 - 用于操作实际建造者,生产产品;
下面看经典的代码:
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 抽象的建造者 * 可以约束真实建造者可能需要执行的流程,一定程度上保证某些流程不被遗漏 * 也可以只有一个produce方法,具体实现交由子类,但这样在一定程度上就放松了约束 * @author panteng * @date 17-2-4. */ public abstract class AbstractBuilder { public abstract void producePart1(); public abstract void producePart2(); public abstract void producePart3(); /** * * @return Product可以是具体的类,也可以是抽象类 */ public abstract Product getProduct(); }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 导演类 * 导演类和产品没有直接关系,和建造者有直接关系,只按照一定的规则操作建造者生产产品 * @author panteng * @date 17-2-4. */ public class Director { private AbstractBuilder builder; public Director(){ } public Director(AbstractBuilder inBuilder){ this.builder = inBuilder; } /** * @description 在此保证建造者必须执行那些流程 * @return */ public Product buildProduct(){ builder.producePart1(); builder.producePart2(); builder.producePart3(); return builder.getProduct(); } public AbstractBuilder getBuilder(){ return builder; } public void setBuilder(AbstractBuilder builder){ this.builder = builder; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 产品对象 * 可以是抽象类 * @author panteng * @date 17-2-4. */ public class Product { public String part1; public String part2; public String part3; public String getPart1(){ return part1; } public void setPart1(String part1){ this.part1 = part1; } public String getPart3(){ return part3; } public void setPart3(String part3){ this.part3 = part3; } public String getPart2(){ return part2; } public void setPart2(String part2){ this.part2 = part2; } /** * Returns a string representation of the object. In general, the * {@code toString} method returns a string that * "textually represents" this object. The result should * be a concise but informative representation that is easy for a * person to read. * It is recommended that all subclasses override this method. * <p> * The {@code toString} method for class {@code Object} * returns a string consisting of the name of the class of which the * object is an instance, the at-sign character `{@code @}', and * the unsigned hexadecimal representation of the hash code of the * object. In other words, this method returns a string equal to the * value of: * <blockquote> * <pre> * getClass().getName() + '@' + Integer.toHexString(hashCode()) * </pre></blockquote> * * @return a string representation of the object. */ @Override public String toString(){ return this.part1 + " - " + this.part2 + " - " + this.part3; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 实际建造者 * @author panteng * @date 17-2-4. */ public class RealBuilder extends AbstractBuilder { private Product product = new Product(); @Override public void producePart1(){ product.setPart1("p1"); } @Override public void producePart2(){ product.setPart2("p2"); } @Override public void producePart3(){ product.setPart3("p3"); } /** * * @return Product可以是具体的类,也可以是抽象类 */ @Override public Product getProduct(){ return product; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 实际建造者 * @author panteng * @date 17-2-4. */ public class RealBuilder2 extends AbstractBuilder { private Product product = new Product(); @Override public void producePart1(){ product.setPart1("p2:1"); } @Override public void producePart2(){ product.setPart2("p2:2"); } @Override public void producePart3(){ product.setPart3("p2:3"); } /** * * @return Product可以是具体的类,也可以是抽象类 */ @Override public Product getProduct(){ return product; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; import org.junit.Test; /** * @description 测试建造者模式 * @author panteng * @date 17-2-4. */ public class BuilderModelTest { @Test public void builderModelTest(){ RealBuilder builder = new RealBuilder(); Director director = new Director(builder); Product product = director.buildProduct(); System.out.println(product); RealBuilder2 builder2 = new RealBuilder2(); director.setBuilder(builder2); product = director.buildProduct(); System.out.println(product); } }
个人认为,以上设计模式有一个不足之处,即一个builder只能生产一个产品;
解决此不足之处的方案:在AbstractBuilder中增加一个设置product的方法,在director的 buildProduct方法中,new一个新的product,然后注入。这样做的缺点是在破坏了导演类与创建产品无关的约定。实践是检验真理的标准,如果这种方案能很好的解决经典模式的不足,更好的在实践中应用,也未尝不可。欢迎各位吐槽
修改后代码如下:
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 抽象的建造者 * 可以约束真实建造者可能需要执行的流程,一定程度上保证某些流程不被遗漏 * 也可以只有一个produce方法,具体实现交由子类,但这样在一定程度上就放松了约束 * @author panteng * @date 17-2-4. */ public abstract class AbstractBuilder { Product product = new Product(); public abstract void producePart1(); public abstract void producePart2(); public abstract void producePart3(); /** * * @return Product可以是具体的类,也可以是抽象类 */ public abstract Product getProduct(); public void setProduct(Product product){ this.product = product; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 实际建造者 * @author panteng * @date 17-2-4. */ public class RealBuilder extends AbstractBuilder { @Override public void producePart1(){ product.setPart1("p1"); } @Override public void producePart2(){ product.setPart2("p2"); } @Override public void producePart3(){ product.setPart3("p3"); } /** * * @return Product可以是具体的类,也可以是抽象类 */ @Override public Product getProduct(){ return product; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 实际建造者 * @author panteng * @date 17-2-4. */ public class RealBuilder2 extends AbstractBuilder { private Product product = new Product(); @Override public void producePart1(){ product.setPart1("p2:1"); } @Override public void producePart2(){ product.setPart2("p2:2"); } @Override public void producePart3(){ product.setPart3("p2:3"); } /** * * @return Product可以是具体的类,也可以是抽象类 */ @Override public Product getProduct(){ return product; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 导演类 * 导演类和产品没有直接关系,和建造者有直接关系,只按照一定的规则操作建造者生产产品 * @author panteng * @date 17-2-4. */ public class Director { private AbstractBuilder builder; public Director(){ } public Director(AbstractBuilder inBuilder){ this.builder = inBuilder; } /** * @description 在此保证建造者必须执行那些流程 * @return */ public Product buildProduct(){ builder.setProduct(new Product()); builder.producePart1(); builder.producePart2(); builder.producePart3(); return builder.getProduct(); } public AbstractBuilder getBuilder(){ return builder; } public void setBuilder(AbstractBuilder builder){ this.builder = builder; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; /** * @description 产品对象 * 可以是抽象类 * @author panteng * @date 17-2-4. */ public class Product { public String part1; public String part2; public String part3; public String getPart1(){ return part1; } public void setPart1(String part1){ this.part1 = part1; } public String getPart3(){ return part3; } public void setPart3(String part3){ this.part3 = part3; } public String getPart2(){ return part2; } public void setPart2(String part2){ this.part2 = part2; } /** * Returns a string representation of the object. In general, the * {@code toString} method returns a string that * "textually represents" this object. The result should * be a concise but informative representation that is easy for a * person to read. * It is recommended that all subclasses override this method. * <p> * The {@code toString} method for class {@code Object} * returns a string consisting of the name of the class of which the * object is an instance, the at-sign character `{@code @}', and * the unsigned hexadecimal representation of the hash code of the * object. In other words, this method returns a string equal to the * value of: * <blockquote> * <pre> * getClass().getName() + '@' + Integer.toHexString(hashCode()) * </pre></blockquote> * * @return a string representation of the object. */ @Override public String toString(){ return super.toString() + " = " + this.part1 + " - " + this.part2 + " - " + this.part3; } }
/* * Copyright (c) 2017. Xiaomi.Co.Ltd All rights reserved */ package com.pt.builder; import org.junit.Test; /** * @description 测试建造者模式 * @author panteng * @date 17-2-4. */ public class BuilderModelTest { @Test public void builderModelTest(){ RealBuilder builder = new RealBuilder(); Director director = new Director(builder); Product product = director.buildProduct(); System.out.println(product); product = director.buildProduct(); System.out.println(product); RealBuilder2 builder2 = new RealBuilder2(); director.setBuilder(builder2); product = director.buildProduct(); System.out.println(product); } }
建造者模式一般在游戏中使用较多:如不同类型的角色,一般属性相同,展现不同; 不同场景的创建,都要创建天、地、物、人等!
===========================设计模式系列文章=========================