12

设计模式系列| 建造者模式

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=Mzg5MjQ5MzY2Mg%3D%3D&%3Bmid=2247485834&%3Bidx=1&%3Bsn=192fe66abc2cbb1fbccc17c801101122
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

大家好,我是狼王,一个爱打球的程序员

这是设计模式的第四篇,这篇让我们来认识一下 建造者模式

1、概述

建造者模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。

2、适用场景

1)避免重叠构造函数,例如:一个类有很多的属性,这时候的构造方法需要传递很多的参数,为了避免这样,会重新写一个相对参数较少的构造方法,但是仍然需要对其他参数进行赋默认值。

2)当需要创建不同的产品类型,此处指比较接近的产品类型,则可以在顶层builder中包含大部分产品属性的赋值方法。

3、实例

有以下场景,我们分别使用常规方式和建造者模式实现:

当前有一个汽车工厂,可以生产的汽车类型有ordinary,sport。
除了汽车之外,同样可以生产汽车的操作手册manual。
汽车有以下的属性:
1、type 类型
2、seats 座位数
3、engine 引擎
4、GPS 导航

分别实现汽车和手册的生产过程

不使用建造者模式

分别创建车car和手册manual,以及其内部的属性,当前例子中车和手册是相同的属性。

@Data
public class Car {

private CarType type;

private int seats;

private Engine engine;

private GPS GPS;

public Car(CarType type, int seats, Engine engine, com.cloud.bssp.designpatterns.builder.withouttdesign.entity.GPS GPS) {
this.type = type;
this.seats = seats;
this.engine = engine;
this.GPS = GPS;
}

public void detail() {
System.out.println("this is " + type + " car");
System.out.println("the seats is :" + seats);
System.out.println("the engine is :" + engine);
System.out.println("this GPS is :" + GPS);
}
}
@Data
public class Manual {

private CarType type;

private int seats;

private Engine engine;

private GPS GPS;

public Manual(CarType type, int seats, Engine engine, com.cloud.bssp.designpatterns.builder.withouttdesign.entity.GPS GPS) {
this.type = type;
this.seats = seats;
this.engine = engine;
this.GPS = GPS;
}

public void detail() {
System.out.println("this is " + type + " car");
System.out.println("the seats is :" + seats);
System.out.println("the engine is :" + engine);
System.out.println("this GPS is :" + GPS);
}
}
public enum CarType {
SPORT,ORDINARY;
}
/**
* 汽车引擎
*/

@Data
public class Engine {

/**
* 排量
*/

private final double volume;
/**
* 里程
*/

private double mileage;

public Engine(double volume, double mileage) {
this.volume = volume;
this.mileage = mileage;
}
}
@Data
public class GPS {
/**
* 路线
*/

private String route;

public GPS(String route) {
this.route = route;
}
}

测试类:

/**
* 客户端
*/

@RunWith(SpringRunner.class)
@SpringBootTest(classes
= TestApplication.class)
public class TestDemo
{

@Test
public void test() {
//生产跑车
Car sportCar = new Car(
CarType.SPORT,
2,
new Engine(3.0, 0),
new GPS("上海东方明珠塔到上海动物园")
);
sportCar.detail();
System.out.println("----------------------------------------");
//生产普通汽车
Car ordinaryCar = new Car(
CarType.ORDINARY,
4,
new Engine(2.0, 0),
new GPS("上海东方明珠塔到上海动物园")
);
ordinaryCar.detail();
System.out.println("----------------------------------------");
//生产汽车操作手册
Manual manual = new Manual(
CarType.ORDINARY,
4,
new Engine(2.0, 0),
new GPS("上海东方明珠塔到上海动物园")
);
manual.detail();
System.out.println("----------------------------------------");
}
}

结果:

this is SPORT car
the seats is :2
the engine is :Engine(volume=3.0, mileage=0.0)
this GPS is :GPS(route=上海东方明珠塔到上海动物园)
----------------------------------------
this is ORDINARY car
the seats is :4
the engine is :Engine(volume=2.0, mileage=0.0)
this GPS is :GPS(route=上海东方明珠塔到上海动物园)
----------------------------------------
this is ORDINARY car
the seats is :4
the engine is :Engine(volume=2.0, mileage=0.0)
this GPS is :GPS(route=上海东方明珠塔到上海动物园)
----------------------------------------

使用建造者模式实现

使用建造者模式后,代码要比上面的方法多了不少:

ruIFzqa.png!mobile

创建顶层Builder

public interface Builder {

void setCarType(CarType carType);

void setSeats(int seats);

void setEngine(Engine engine);

void setGPS(GPS gps);
}

创建实体类,与上面是相同的,这里不重复了。

创建car的builder:

@Data
public class CarBuilder implements Builder {

private CarType carType;

private int seats;

private Engine engine;

private GPS GPS;

public Car getResult() {
return new Car(carType, seats, engine, GPS);
}
}

创建汽车操作手册builder:

@Data
public class ManualBuilder implements Builder {

private CarType carType;

private int seats;

private Engine engine;

private GPS GPS;

public Manual getResult() {
return new Manual(carType, seats, engine, GPS);
}
}

创建一个builder管理器:

public class Director {

public void constructSportsCar(Builder builder) {
builder.setCarType(CarType.SPORT);
builder.setSeats(2);
builder.setEngine(new Engine(3.0, 0));
builder.setGPS(new GPS("上海东方明珠塔到上海动物园"));
}

public void constructOrdinaryCar(Builder builder) {
builder.setCarType(CarType.ORDINARY);
builder.setSeats(4);
builder.setEngine(new Engine(2.0, 0));
builder.setGPS(new GPS("上海东方明珠塔到上海动物园"));
}
}

测试类:

@RunWith(SpringRunner.class)
@SpringBootTest(classes
= TestApplication.class)
public class TestDemo
{

@Test
public void test() {
Director director = new Director();
//生产跑车
CarBuilder carBuilder = new CarBuilder();
director.constructSportsCar(carBuilder);
Car sportCar = carBuilder.getResult();
sportCar.detail();
System.out.println("-----------------------------------");

//生产普通汽车
director.constructOrdinaryCar(carBuilder);
Car ordinaryCar = carBuilder.getResult();
ordinaryCar.detail();
System.out.println("-----------------------------------");

//生产汽车操作手册
ManualBuilder manualBuilder = new ManualBuilder();
director.constructOrdinaryCar(manualBuilder);
Manual manual = manualBuilder.getResult();
manual.detail();
System.out.println("-----------------------------------");
}
}

结果:

this is SPORT car
the seats is :2
the engine is :Engine(volume=3.0, mileage=0.0)
this GPS is :GPS(route=上海东方明珠塔到上海动物园)
-----------------------------------
this is ORDINARY car
the seats is :4
the engine is :Engine(volume=2.0, mileage=0.0)
this GPS is :GPS(route=上海东方明珠塔到上海动物园)
-----------------------------------
this Manual ORDINARY car
the Manual seats is :4
the Manual engine is :Engine(volume=2.0, mileage=0.0)
this GManual PS is :GPS(route=上海东方明珠塔到上海动物园)
-----------------------------------

4、分析

建造者模式基本有以下几个角色:

vEzIfaa.png!mobile

如上面两种方式的结果显示,都可以实现生产汽车和汽车手册。

结果没什么差异。

在没使用建造者的方式中:

生产汽车的参数是由客户端自己指定的,并且需要传很多的参数,实际工作中可能需要更多的参数,可能有部分参数是不需要的。

使用建造者模式

用户不需要去具体指定多个参数了,对于客户端更友好。

builder:将产品new()提出到builder中,提供产品所拥有的属性设置方法,一类产品都可以使用这个builder进行产品创建。

director:作为builder的管理者,主要控制产品的属性设置,在这个类中,具体指定除了可以生产的产品的构造,并且对属性进行赋值,最终返回一个用户需要的builder。

客户端调用只需要创建需要的产品类型builder,通过管理者director对builder进行属性设置,最终客户端通过调用builder的方法获取最终需要的产品。

极大程度减少并优化的客户端的代码,同时由管理者director限制了产品的种类。

从扩展层层面看:

未使用建造者:增加对应的产品类,客户端直接new。

使用建造者模式:增加builder,并且在director增加可创建的产品的builder构造。

5、总结

最后总结下上面例子中使用抽象工厂方法的优缺点:

优点:

1)遵守单一原则。

2)不同产品,可复用相同的产品创建流程。

3)简化客户端调用方式。去除多参构造的方式。

4)分步骤创建对象。

缺点:

增加多个类,代码复杂度增加。

至此这篇建造者模式就到这结束了,后续还会继续更新该系列,有疑问的也可以加我好友一起交流哦!

乐于输出干货的Java技术公众号: 狼王编程 。公众号内有大量的技术文章、海量视频资源、精美脑图,不妨来关注一下!回复 资料 领取大量学习资源和免费书籍!

Zr2iYvQ.jpg!mobile

转发朋友圈是对我最大的支持!

觉得有点东西就点一下“赞和在看”吧!感谢大家的支持了!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK