参考链接:建造者(Builder)模式
1. 设计意图及特点 建造者(Builder)模式的定义:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
一句话概括: 将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的意图是为了构造对象,因此它属于创建型模式。
例如对一个迷宫可能有墙、房间和门,并且数量不计。迷宫可能仅由一堵墙构成,也可能由两堵墙构成,也可能由两个房间加一扇门构成…如果采用重载的方式生产迷宫,代码量是难以计数的、无比庞大的。
针对一个对象拥有大量的组件(迷宫可以拥有很多墙或房间或门),而构造这个对象对其组件的使用又是不确定的这一问题(使用墙、房间、门的数量是不确定的),想要精细的控制构建过程,此时可以采用建造者模式解决问题。
优点
封装性良好,构建和表示分离。
扩展性比较好,各个具体的建造者相互独立,有利于系统的解耦。
客户端没必要知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
缺点
产品的组成部分必须相同,这限制了其使用范围。
如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
为了创建对象,必须先创建它的构建器,在十分注重性能的情况下,这是一笔性能开销。
注意
建造者模式和工厂模式的关注点不同: 建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
2. 适用场景
构造过程中,被构造的对象具有不同的表示。
一些基本部件不会变,而其组合经常变化的时候。
需要生成的对象内部属性本身相互依赖。
3. 经典建造者模式 建造者模式分为两种,一种为经典建造者模式,另一种为变种建造者模式。
我们日常开发中经典建造者模式一般不常用,用的比较多的是变种的建造者模式。所以我们应该重点关注变种的建造者模式。
3.1 模式的结构 建造者(Builder)模式的主要角色如下:
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()
。
具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
建造者模式的结构图:
3.2 代码实现 首先我们先来创建一个Product类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class Product { private String partA; private String partB; private String partC; public Product ( ) { } public Product (String partA, String partB, String partC) { this .partA = partA; this .partB = partB; this .partC = partC; } public void setPartA (String partA) { this .partA = partA; } public void setPartB (String partB) { this .partB = partB; } public void setPartC (String partC) { this .partC = partC; } public String getPartA () { return partA; } public String getPartB () { return partB; } public String getPartC () { return partC; } public void show () { System.out.println("产品的部件A=" + partA); System.out.println("产品的部件B=" + partB); System.out.println("产品的部件C=" + partC); } }
创建一个抽象的生产产品过程的Builder类:
1 2 3 4 5 6 7 8 9 10 11 12 public abstract class ProductBuilder { public abstract void builderPartA () ; public abstract void builderPartB () ; public abstract void builderPartC () ; public abstract Product getResult () ; }
实体建造者类,可以根据需求构建出多个实体建造者类,在此只构建一个 ConcreteBuilder1 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class ConcreteBuilder1 extends ProductBuilder { private Product product; public ConcreteBuilder1 () { this .product = new Product (); } @Override public void builderPartA () { product.setPartA("partA" ); } @Override public void builderPartB () { product.setPartB("partB" ); } @Override public void builderPartC () { product.setPartC("partC" ); } @Override public Product getResult () { return product; } }
指导者类:
1 2 3 4 5 6 7 8 9 10 11 12 public class ProductDirect { public void Director (ProductBuilder builder) { builder.builderPartA(); builder.builderPartB(); builder.builderPartC(); } }
测试:
1 2 3 4 5 6 7 8 @Test public void testProduct () { ProductDirect direct = new ProductDirect (); ConcreteBuilder1 builder = new ConcreteBuilder1 (); direct.Director(builder); Product product = builder.getResult(); product.show(); }
测试结果:
1 2 3 产品的部件A=partA 产品的部件B=partB 产品的部件C=partC
4. 变种建造者模式 4.1 如何使用 以 Person 为例:
在 Person 中创建一个静态嵌套类 Builder,并将 Person 中的参数都复制到 Builder 类中。
在 Person 中提供一个 非 public
的构造函数,参数类型为 Builder。
在 Builder 中提供一个 public
的构造函数,参数为构造 Person 对象时的必填参数。
在 Builder 中提供多个设置方法,用于对 Person 中可选参数进行赋值,返回值为 Builder 类型的实例(用于链式调用)。
在 Builder 中提供一个 build() 方法(当然叫啥名都可以,作为构建方法),用于构建最终的 Person 实例并返回。
4.2 代码实现 新建一个Person 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public class Person { private String name; private String gender; private String age; private String career; private Person (Builder builder) { this .name = builder.name; this .gender = builder.gender; this .age = builder.age; this .career = builder.career; } public String getName () { return name; } public String getGender () { return gender; } public String getAge () { return age; } public String getCareer () { return career; } public static class Builder { private String name; private String gender; private String age; private String career; public Builder (String name, String gender) { this .name = name; this .gender = gender; } public Builder age (String age) { this .age = age; return this ; } public Builder career (String career) { this .career = career; return this ; } public Person build () { return new Person (this ); } } }
测试:
1 2 3 4 5 6 7 8 9 10 11 @Test public void testBuilder () { Person person = new Person .Builder("木又枯了" , "男" ) .age("18" ) .career("小学生" ) .build(); System.out.println("Name = " + person.getName()); System.out.println("Gender = " + person.getGender()); System.out.println("Age = " + person.getAge()); System.out.println("Career = " + person.getCareer()); }
测试结果:
1 2 3 4 Name = 木又枯了 Gender = 男 Age = 18 Career = 小学生
说明
相比于经典建造者模式,变种建造者模式省略了 Director 这个角色,将构建算法交给了 Client 端,其次将 Builder 写到了要构建的产品类里面,最后采用了链式调用。
上述实现中的 Person 对象的属性值都是无法修改的,因此建造者模式也常用于创建 一个成员变量不可变的对象 。所以在最初的建造者模式中,常把 Person 的属性设置成 final
的,Person类中定义的与其属性相同的内部类Builder中必须具备的属性也需要用 final
修饰,防止这些属性没有被赋值,其他非必须的属性不能用final,因为如果加了final,就必须对其进行初始化,这样这些非必须的属性又变成必须的。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Person { private final String name; private final String gender; private final String age; private final String career; public static class Builder { private final String name; private final String gender; private String age; private String career; } }