本篇Blog继续学习创建型模式,了解如何更优雅的创建对象,本篇学习的是建造者模式。由于学习的都是设计模式,所有系列文章都遵循如下的目录:
模式档案:包含模式的定义、模式的特点、解决什么问题、优缺点、使用场景模式结构:包含模式的角色定义及调用关系模式示例:包含模式的实现方式代码举例模式对比:如果模式相似,有必要体现其相似点,区分使用模式实践:如果工作中或开源项目用到了该模式,就将使用过程贴到这里,并且客观讨论使用的是否恰当
接下来所有设计模式的介绍都暂且遵循此基本行文逻辑吗,对于模式实践有则增加,没有就不需要了。
模式档案模式定义:建造者模式(Builder Pattern)指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的
模式特点:主要特点是将变与不变分离开,建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
解决什么问题:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定
优点:封装性好,构建和表示分离;扩展性好,各个具体的建造者相互独立,有利于系统的解耦;客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
缺点:产品的组成部分必须相同,这限制了其使用范围;如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
使用场景: 当需要生成的对象具有复杂的内部结构且需要生成的对象内部属性本身相互依赖。
相同的方法,不同的执行顺序,产生不同的结果。多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。初始化一个对象特别复杂,参数多,而且很多参数都具有默认值
符合以上条件可以考虑建造者模式
模式结构建造者(Builder)模式由产品、抽象建造者、具体建造者、指挥者 4 个角色构成
产品(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
角色的相互调用关系如下图所示:
产品:包含多个组成部件的复杂对象
package com.example.designpattern.builder;
import lombok.Setter;
@Setter
public class Product {
private String partA;
private String partB;
private String partC;
public void show(){
System.out.println(partA+" ; "+partB+" ; "+partC);
}
}
抽象建造者
抽象建造者:包含创建产品各个子部件的抽象方法
package com.example.designpattern.builder;
public abstract class Builder {
//创建产品对象
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult() {
return product;
}
}
具体建造者
具体建造者:实现了抽象建造者接口
package com.example.designpattern.builder;
public class ConcreteBuilder extends Builder {
public void buildPartA() {
product.setPartA("建造 PartA");
}
public void buildPartB() {
product.setPartB("建造 PartB");
}
public void buildPartC() {
product.setPartC("建造 PartC");
}
}
指挥者
指挥者:调用建造者中的方法完成复杂对象的创建
package com.example.designpattern.builder;
import lombok.AllArgsConstructor;
// 将一个复杂的构建过程与其表示相分离
@AllArgsConstructor
public class Director {
// 针对接口编程,而不是针对实现编程
private Builder builder;
//产品构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
客户类
package com.example.designpattern.builder;
public class Client {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
product.show();
}
}
调用结果如下:
建造 PartA ; 建造 PartB ; 建造 PartC Process finished with exit code 0模式示例
用建造者(Builder)模式描述客厅装修。客厅装修是一个复杂的过程,它包含墙体的装修、电视机的选择、沙发的购买与布局等。客户把装修要求告诉项目经理,项目经理指挥装修工人一步步装修,最后完成整个客厅的装修与布局。
客厅是产品,包括墙、电视和沙发等组成部分。抽象装修工人是抽象建造者,这里并没有实例对应,可以理解为装修工人们的培训师傅吧。具体装修工人是具体建造者,他们负责装修与墙、电视和沙发的布局。项目经理是指挥者,他负责指挥装修工人进行装修。
另外,客厅类中提供了 show() 方法,可以将装修效果图显示出来。
整体代码结构如下:
//产品:客厅
@Setter
class Parlour {
private String wall; //墙
private String TV; //电视
private String sofa; //沙发
public void show() {
System.out.println(wall+" ; "+TV+" ; "+sofa);
}
}
//抽象建造者:装修工人
abstract class Decorator {
//创建产品对象
protected Parlour product = new Parlour();
public abstract void buildWall();
public abstract void buildTV();
public abstract void buildSofa();
//返回产品对象
public Parlour getResult() {
return product;
}
}
//具体建造者:具体装修工人1
class ConcreteDecorator1 extends Decorator {
public void buildWall() {
product.setWall("wall1");
}
public void buildTV() {
product.setTV("TV1");
}
public void buildSofa() {
product.setSofa("sofa1");
}
}
//具体建造者:具体装修工人2
class ConcreteDecorator2 extends Decorator {
public void buildWall() {
product.setWall("wall2");
}
public void buildTV() {
product.setTV("TV2");
}
public void buildSofa() {
product.setSofa("sofa2");
}
}
//指挥者:项目经理
class ProjectManager {
private Decorator builder;
public ProjectManager(Decorator builder) {
this.builder = builder;
}
//产品构建与组装方法
public Parlour decorate() {
builder.buildWall();
builder.buildTV();
builder.buildSofa();
return builder.getResult();
}
}
//客户端
package com.example.designpattern.builder;
public class Client {
public static void main(String[] args) {
Decorator builder = new Decorator();
ProjectManager director = new ProjectManager(builder);
Parlour product = director.construct();
product.show();
}
}
模式对比
其实通篇学习下来感觉工厂模式和建造者模式很相似,都是将对象与创建过程分离,那么他们有什么区别呢?
建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。
总结一句话,工厂方法模式的子类只创建一个对象,不关注内部细节,关注的是不同子类工厂创建的对象不同,而建造者模式则关注对象的内部创造细节,如果说相似,抽象工厂模式和建造者模式比较相似,如果抽象工厂模式的产品族和建造者模式的复杂对象是一个层级,那么抽象工厂产品族里的产品可以对标建造者模式复杂对象的成员,但即便这样,抽象工厂模式也不提现产品族内的产品组装顺序,而建造者模式依旧需要关注,拿上边的例子举例:
有的客户可能不需要电视,那装修工人就不setTV值有的客户可能先购买了沙发才能决定电视选择哪个,那么装修工人就必须先setSofa,然后依据sofa的值再setTV
所以建造者模式构建对象会关注更多细节。
总结一下和工厂模式类似,建造者模式也致力于将对象的创建与使用分离。不同的是工厂模式的产品是具有抽象产品的,也就是具体产品可以有各自不同的方法实现,建造者模式的产品是固定的复杂对象,不同的具体建造者所能改变的也只是产品的组成部分属性值,产品的方法实现是固定的。但建造者模式拥有指挥者角色可以依据不同场景下的要求更好的调节产品内组成部分的依赖(顺序或种类)从而改变产品的特征。我感觉将二者结合起来应对复杂场景设计应该非常有帮助。



