场景
建造者模式
复杂对象的组装与创建
没有人买车会只买一个轮胎或者方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。
如何将这些部件组装成一辆完整的汽车并返回给用户,这是建造者模式需要解决的问题。
建造者模式又称为生成器模式,它是一种较为复杂、使用频率也相对较低的创建型模式。
建造者模式为客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品。
建造者模式是较为复杂的创建型模式,它将客户端与包含多个组成部分(或部件)的复杂对象的创建过程分离,
客户端无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可。
它关注如何一步一步创建一个的复杂对象,不同的具体建造者定义了不同的创建过程,
且具体建造者相互独立,增加新的建造者非常方便,无须修改已有代码,系统具有较好的扩展性。
建造者模式举例
以角色扮演类游戏为例,需要对游戏角色进行设计,而且随着该游戏的升级将不断增加新的角色。
不同类型的游戏角色,其性别、脸型、服装等外部特性都有所差异。
开发一个小工具来创建游戏角色,可以创建不同类型的角色并可以灵活增加新的角色。
游戏角色是一个复杂对象,它包含性别、脸型等多个组成部分,不同的游戏角色其组成部分有所差异。
无论是何种造型的游戏角色,它的创建步骤都大同小异,都需要逐步创建其组成部分,
再将各组成部分装配成一个完整的游戏角色。如何一步步创建一个包含多个组成部分的复杂对象,
建造者模式为解决此类问题而诞生。
建造者模式结构图与角色
建造者模式中包含如下角色
Builder(抽象建造者):
它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,
一类方法是buildPartX(),它们用于创建复杂对象的各个部件;
另一类方法是getResult(),它们用于返回复杂对象。
Builder既可以是抽象类,也可以是接口。
ConcreteBuilder(具体建造者):
它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,
也可以提供一个方法返回创建好的复杂产品对象。
在ConcreteBuilder中实现了buildPartX()方法,通过调用Product的setPartX()方法可以给产品对象的成员属性设值。
不同的具体建造者在实现buildPartX()方法时将有所区别,如setPartX()方法的参数可能不一样,
在有些具体建造者类中某些setPartX()方法无须实现(提供一个空实现)。
而这些对于客户端来说都无须关心,客户端只需知道具体建造者类型即可。
Product(产品角色):
它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
复杂对象是指那些包含多个成员属性的对象,这些成员属性也称为部件或零件,如汽车包括方向盘、发动机、轮胎等部件,
电子邮件包括发件人、收件人、主题、内容、附件等部件
Director(指挥者):
指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,
可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。
客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),
然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
该类主要有两个作用:
一方面它隔离了客户与创建过程;
另一方面它控制产品的创建过程,包括某个buildPartX()方法是否被调用以及多个buildPartX()方法调用的先后次序等。
指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。
在实际生活中也存在类似指挥者一样的角色,如一个客户去购买电脑,电脑销售人员相当于指挥者,只要客户确定电脑的类型,
电脑销售人员可以通知电脑组装人员给客户组装一台电脑。
建造者模式与工厂模式有何区别?
建造者模式与抽象工厂模式有点相似,但是建造者模式返回一个完整的复杂产品,而抽象工厂模式返回一系列相关的产品;
在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,
客户端通过指定具体建造者类型并指导Director类如何去生成对象,侧重于一步步构造一个复杂对象,然后将结果返回。
如果将抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车。
设计模式-简单工厂模式、工厂模式、抽象工厂模式在Java中的使用示例:
设计模式-简单工厂模式、工厂模式、抽象工厂模式在Java中的使用示例_java工厂模式应用场景举例_霸道流氓气质的博客-CSDN博客
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
实现
下面使用建造者模式实现上面角色设计的流程
1、新建Actor充当复杂产品,这里使用lombok省略get和set方法。
import lombok.Data;
/**
* 充当复杂产品
*/
@Data
public class Actor {
//角色类型
private String type;
//性别
private String sex;
//脸型
private String face;
//服装
private String costume;
}
2、新建抽象构造者ActorBuilder,并新增方法,返回一个完整的游戏角色对象
/**
* 抽象构造者
*/
public abstract class ActorBuilder {
protected Actor actor = new Actor();
public abstract void buildType();
public abstract void buildSex();
public abstract void buildFace();
public abstract void buildContume();
//工厂方法,返回一个完整的游戏角色对象
public Actor createActor(){
return actor;
}
}
3、具体建造者一共有三种角色:英雄、天使、恶魔
英雄角色构造器实现
/**
* 英雄角色建造器:具体建造者
*/
public class HeroBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("英雄");
}
@Override
public void buildSex() {
actor.setSex("男");
}
@Override
public void buildFace() {
actor.setFace("帅气");
}
@Override
public void buildContume() {
actor.setCostume("盔甲");
}
}
天使角色构造器实现
/**
* 天使角色建造器:具体建造者
*/
public class AngelBuilder extends ActorBuilder{
@Override
public void buildType() {
actor.setType("天使");
}
@Override
public void buildSex() {
actor.setSex("女");
}
@Override
public void buildFace() {
actor.setFace("漂亮");
}
@Override
public void buildContume() {
actor.setCostume("翅膀");
}
}
恶魔角色构造器实现
/**
* 恶魔角色建造器:具体建造者
*/
public class DeviBuilder extends ActorBuilder
{
@Override
public void buildType() {
actor.setType("恶魔");
}
@Override
public void buildSex() {
actor.setSex("男");
}
@Override
public void buildFace() {
actor.setFace("吓人");
}
@Override
public void buildContume() {
actor.setCostume("皮衣");
}
}
4、新建指挥者-角色创建控制器
/**
* 角色创建控制器:指挥者
*/
public class ActorController{
//逐步构建复杂产品对象
public Actor construct(ActorBuilder builder){
Actor actor;
builder.buildType();
builder.buildSex();
builder.buildFace();
builder.buildContume();
actor = builder.createActor();
return actor;
}
}
5、客户端调用方式如下
//针对抽象构造者编程
ActorBuilder actorBuilder;
//通过配置文件或其他方式获取具体的建造者
actorBuilder = new HeroBuilder();
ActorController actorController = new ActorController();
Actor actor;
actor = actorController.construct(actorBuilder);
System.out.println(actor);
6、总结
在建造者模式中,客户端只需实例化指挥者类,指挥者类针对抽象建造者编程,客户端根据需要传入
具体的建造者类型,指挥者将指导具体建造者一步一步构造一个完整的产品(逐步调用具体建造者的
buildX()方法),相同的构造过程可以创建完全不同的产品。在游戏角色实例中,如果需要更换角
色,只需要修改配置文件,更换具体角色建造者类即可;如果需要增加新角色,可以增加一个新的具
体角色建造者类作为抽象角色建造者的子类,再修改配置文件即可,原有代码无须修改,完全符合“开
闭原则”。
建造者模式的主要优点如下:
(1) 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使
得相同的创建过程可以创建不同的产品对象。
(2) 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者
或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽
象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”
(3) 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过
程更加清晰,也更方便使用程序来控制创建过程
建造者模式的主要缺点如下:
(1) 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很
大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
(2) 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变
得很庞大,增加系统的理解难度和运行成本。
适用场景
在以下情况下可以考虑使用建造者模式:
(1) 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
(2) 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
(3) 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装
在指挥者类中,而不在建造者类和客户类中。
(4) 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。