手撕设计模式——复杂对象之生成器模式
1.业务需求
大家好,我是菠菜啊,好久不见,今天给大家带来的是创建型模式——生成器模式。老规矩,在介绍这期生成器模式前,我们先来看看这样的需求:现在有一个汽车装配的流水线,要支持轮胎、座椅、发动机等配件客户的定制,我们该怎么设计?
2.代码实现
实现初步思路:
上一章我们学习了工厂方法模式(看这篇文章前可以先回顾《设计模式——工厂三兄弟之工厂方法》这篇),我们首先会想到用工厂模式实现。但是仔细一想,工厂是侧重一系列对象创建的方式,而上述需求是注重对象内部的创建细节、组成部分、创建步骤等。这样显然不合适,所以初级版本如下。
初始版本:
public class Car {
//发动机
private Engine engine;
//轮胎
private Tyre tyre;
//座椅
private Chair chair;
public void setEngine(Engine engine) {
this.engine = engine;
}
public void setTyre(Tyre tyre) {
this.tyre = tyre;
}
public void setChair(Chair chair) {
this.chair = chair;
}
public Engine getEngine() {
return engine;
}
public Tyre getTyre() {
return tyre;
}
public Chair getChair() {
return chair;
}
public Car() {
}
public Car(Engine engine, Tyre tyre, Chair chair) {
this.engine = engine;
this.tyre = tyre;
this.chair = chair;
}
public void showCar() {
System.out.println(this);
}
@Override
public String toString() {
return "Car{" +
"engine=" + engine +
", tyre=" + tyre +
", chair=" + chair +
'}';
}
}
//发动机
public class Engine {
//品牌
private String brand;
//型号
private String model;
//排量
private double displacement;
public Engine(String brand, String model, double displacement) {
this.brand = brand;
this.model = model;
this.displacement = displacement;
}
@Override
public String toString() {
return "Engine{" +
"brand='" + brand + ''' +
", model='" + model + ''' +
", displacement=" + displacement +
'}';
}
}
//轮胎
public class Tyre {
//品牌
private String brand;
//型号
private String model;
//尺寸
private int size;
public Tyre(String brand, String model, int size) {
this.brand = brand;
this.model = model;
this.size = size;
}
@Override
public String toString() {
return "Tyre{" +
"brand='" + brand + ''' +
", model='" + model + ''' +
", size=" + size +
'}';
}
}
//座椅
public class Chair {
//品牌
private String brand;
//型号
private String model;
//配置
private String configure;
public Chair(String brand, String model, String configure) {
this.brand = brand;
this.model = model;
this.configure = configure;
}
@Override
public String toString() {
return "Chair{" +
"brand='" + brand + ''' +
", model='" + model + ''' +
", configure='" + configure + ''' +
'}';
}
}
//客户端
public class Client {
public static void main(String[] args) {
Car car1=new Car(new Engine("Benz","E200",2.5),new Tyre("韩泰","T200",18),new Chair("奔驰","C200","高配"));
car1.showCar();
System.out.println("------------------------------------------------------------------------");
Car car2=new Car(new Engine("宝马","325Li",3.5),new Tyre("米其林","T300",19),new Chair("宝马","325","低配"));
car2.showCar();
}
}
实现代码结构图:
思考:上述代码会发现,汽车对象的创建和表示没有分离,建造一辆汽车太复杂了,有上百种配件,如果要这么制造客户端知道的细节太多了,代码的耦合性太强了,并且生产组装一辆汽车的步骤和配件基本上一样的,可以抽象出来,于是有了以下升级方案。
3.方案改进
实现代码结构图:
汽车主管类:
public class CarDirector {
private CarBuilder carBuilder;
public CarDirector(CarBuilder carBuilder) {
this.carBuilder = carBuilder;
}
public Car build() {
carBuilder.buildChair();
carBuilder.buildEngine();
carBuilder.buildTyre();
return carBuilder.getCar();
}
}
汽车生成器抽象类:
public abstract class CarBuilder {
protected Car car=new Car();
public abstract void buildEngine();
public abstract void buildTyre();
public abstract void buildChair();
public Car getCar(){
return car;
}
}
奥迪汽车生成器类:
public class AudiConcreteCarBuilder extends CarBuilder {
@Override
public void buildEngine() {
car.setEngine(new Engine("奥迪","A4L",2.0));
}
@Override
public void buildTyre() {
if(car.getEngine()== null){
throw new RuntimeException("必须先安装发动机");
}
car.setTyre(new Tyre("朝阳","T300",17));
}
@Override
public void buildChair() {
if(car.getEngine()== null){
throw new RuntimeException("必须先安装发动机");
}
if(car.getTyre()== null){
throw new RuntimeException("必须先安装轮胎");
}
car.setChair(new Chair("奥迪","A4L","低配"));
}
}
奔驰汽车生成器类:
public class BenzConcreteCarBuilder extends CarBuilder {
@Override
public void buildEngine() {
car.setEngine(new Engine("奔驰","E200",2.5));
}
@Override
public void buildTyre() {
if(car.getEngine()== null){
throw new RuntimeException("必须先安装发动机");
}
car.setTyre(new Tyre("韩泰","T200",18));
}
@Override
public void buildChair() {
if(car.getEngine()== null){
throw new RuntimeException("必须先安装发动机");
}
if(car.getTyre()== null){
throw new RuntimeException("必须先安装轮胎");
}
car.setChair(new Chair("奔驰","C200","高配"));
}
}
宝马汽车生成器类:
public class BMWConcreteCarBuilder extends CarBuilder {
@Override
public void buildEngine() {
car.setEngine(new Engine("宝马","325Li",2.0));
}
@Override
public void buildTyre() {
if(car.getEngine()== null){
throw new RuntimeException("必须先安装发动机");
}
car.setTyre(new Tyre("powertec","T300",17));
}
@Override
public void buildChair() {
if(car.getEngine()== null){
throw new RuntimeException("必须先安装发动机");
}
if(car.getTyre()== null){
throw new RuntimeException("必须先安装轮胎");
}
car.setChair(new Chair("宝马","325","低配"));
}
}
client类:
public class Client2 {
public static void main(String[] args) {
CarDirector carDirector1=new CarDirector(new BMWConcreteCarBuilder());
Car car1=carDirector1.build();
car1.showCar();
System.out.println("------------------------------------------------------------------------");
CarDirector carDirector2=new CarDirector(new BenzConcreteCarBuilder());
Car car2=carDirector2.build();
car2.showCar();
System.out.println("------------------------------------------------------------------------");
CarDirector carDirector3=new CarDirector(new AudiConcreteCarBuilder());
Car car3=carDirector3.build();
car3.showCar();
}
}
思考:如果安装上述代码的实现,如果要组装一辆车,只需要告诉汽车主管组装什么类型的车(也就是指定具体的生成器)就行了,使用者不需要知道建造的全部细节,这种设计模式叫做——生成器模式,又叫建造者模式(Builder Pattern)。
4.定义和组成结构
建造者模式(Builder Pattern)指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
主要角色:
- 产品(Product):具体生成器要构造的复杂对象;
- 抽象生成器(Builder):抽象生成器是一个接口,该接口除了为创建一个Product对象的各个组件定义了若干个方法外,还要定义返回Product对象的方法
- 具体生成器(ConcreteBuilder):实现了Builder接口的类,具体生成器将实现Builder接口所定义的方法。;
- 指挥者(Director):指挥者是一个类,该类需含有Builder接口声明的变量。指挥者的职责是负责向用户提供具体生成器,即指挥者将请求具体生成器来构造用户所需要的Product对象,如果所请求的具体对象生成器成功德构造出Product对象,指挥者就可以让该具体生成器返回所构造的Product对象。
5.优缺点以及应用场景
优点:
- 产品使用者和创建者解耦,无须知道产品的内部的组成细节,对象的构建过程与表示分离(使用相同的构建过程来创建不同的表示)
- 扩展性好,各个具体的创建者相互独立,满足单一原则和开闭原则
缺点:
- 产品对象的组成部分或创建步骤必须相同
- 产品的内部发生变化,建造者也要同步修改,维护成本高
- 引入了多个抽象和具体类,增加了系统的复杂度和理解难度
适用场景:
- 对象创建复杂、多步骤创建
- 根据不同需求创建内部细节不同对象
现实应用场景:
- StringBuilder和StringBuffer:这俩个类都提供了动态生成字符串的构建功能,高效地创建和拼接字符串。
- 数据库查询构造器:如 MyBatis 中的
SqlBuilder
、Hibernate 的CriteriaBuilder
,都使用了建造者模式来构建复杂的查询语句。
6.和工厂模式对比
- 关注点不同:工厂模式关注的是对象的创建,建造者模式关注的是对象的构造过程。
- 复杂度不同:工厂模式通常用于创建单个对象,建造者模式用于创建具有复杂结构的对象。
- 使用场景不同:工厂模式适用于当创建逻辑简单且对象创建后直接可用的情况,建造者模式适用于需要一步步构建复杂对象的情况。
【个人链接】
微信公众号:
CSDN博客:
个人博客网站:
你的收藏和点赞就是我最大的创作动力,关注我我会持续输出更新!
友情提示:请尊重作者劳动成果,如需转载本博客文章请注明出处!谢谢合作!
【作者:我爱吃菠菜】