设计模式-生成器模式的实现[JAVA]
摘要
The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the Builder design pattern is to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.
可以简单理解为:生成器模式是解决复杂对象创建一种手段,把复杂对象的创建和表示分离,进行解耦,那么为什么要把创建和表示分离呢,因为一旦一个对象有几十个属性需要在构造函数里上送,写起来就很麻烦,也容易出错。
引言
使用一般方式创建复杂对象
- 示例 1
/**
* 简单演示普通创建对象的方式复杂性
* @Author: Shiraki
* @Date: 2020/9/16 13:42
*/
public class DemoBuilder {
public static void main(String[] args) {
System.out.println(
"CellPhone: " +
new CellPhone(new Company("huawei", "010101", "mail.huawei.com"),
new NetworkType(),
new Color(), 10, 20.0));
}
}
class CellPhone {
// 生产商
private Company manufacturer;
// 网络类型
private NetworkType networkType;
// 颜色
private Color color;
// 重量
private int weight;
// 价格
private Double price;
// 普通的构建方法
CellPhone(Company manufacturer, NetworkType networkType, Color color, int weight, Double price) {
this.manufacturer = manufacturer;
this.networkType = networkType;
this.color = color;
this.weight = weight;
this.price = price;
}
@Override
public String toString() {
return "CellPhone{" +
"manufacturer=" + manufacturer +
", networkType=" + networkType +
", color=" + color +
", weight=" + weight +
", price=" + price +
'}';
}
}
class Company {
// 公司名称
private String name = "unknown";
// 公司代码
private String code = "unknown";
// 公司邮箱
private String mail = "unknown";
public Company(String name, String code, String mail) {
this.name = name;
this.code = code;
this.mail = mail;
}
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", code='" + code + '\'' +
", mail='" + mail + '\'' +
'}';
}
}
class NetworkType {
// 运行商
private Company isp = new Company("China Mobile", "10086", "mail.10086.cn");
private String type;
@Override
public String toString() {
return "NetworkType{" +
"isp=" + isp +
", type='" + type + '\'' +
'}';
}
}
class Color {
private int red;
private int green;
private int blue;
@Override
public String toString() {
return "Color{" +
"red=" + red +
", green=" + green +
", blue=" + blue +
'}';
}
}
示例1中创建一个手机需要设置好多个属性,如生产商、网络类型、颜色、重量等。该例子中手机就是一个复杂类型。如果通过普通的构造函数进行初始化,代码写起来非常麻烦,同时还需要调用方去维护
Company
、NetworkType
、Color
等辅助创建手机的类。那么有什么办法可以解决这个问题呢?
答案是生成器-Builder
模型。
生成器Builder模式
基本的生成器模型
1. 类图
Builder
抽象负责对象熟悉分类
Direct
通过builder实例进行特征对象构建-简化对象构建,提供一个类似模板的方法construct
ConcreteBuilder
实际生成器,构建实际产品,但是往往这是构建的产品也是一个抽象产品Iproduct
Product
实际产品,一般开发中此处为产品接口
2. 实现
实现类图:
此处也能看出产品Product
CellPhone
只是一个抽象接口。实际通过CellPhone
接口去创建HuaWeCellPhone
代码如下:
/**
* 通过构建华为手机来掩饰生成器模型
* @Author: Shiraki
* @Date: 2020/9/16 13:42
*/
public class DemoBuilder {
public static void main(String[] args) {
CellBuilder builder = new HuaWeiCellPhoneBuilder();
// 通过HonorCellPhoneDirect
HonorCellPhoneDirect direct = new HonorCellPhoneDirect(builder);
direct.construct("China Union", "5G");
System.out.println("direct.cellBuilder.build() = " + direct.cellBuilder.build());
}
}
abstract class CellPhone {
// 生产商
private Company manufacturer;
// 网络类型
private NetworkType networkType;
// 颜色
private Color color;
// 重量
private int weight;
// 价格
private Double price;
// logo
abstract String getLog();
protected CellPhone() {
}
@Override
public String toString() {
return "\nCellPhone{" +
"\n\tmanufacturer=" + manufacturer +
"\n\tnetworkType=" + networkType +
"\n\tcolor=" + color +
"\n\tweight=" + weight +
"\n\tprice=" + price +
"\n}";
}
public Company getManufacturer() {
return manufacturer;
}
public void setManufacturer(Company manufacturer) {
this.manufacturer = manufacturer;
}
public NetworkType getNetworkType() {
return networkType;
}
public void setNetworkType(NetworkType networkType) {
this.networkType = networkType;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
class HuaWeiCellPhone extends CellPhone {
@Override
String getLog() {
return "菊花";
}
}
// 公司
class Company {
// 公司名称
private String name = "unknown";
// 公司代码
private String code = "unknown";
// 公司邮箱
private String mail = "unknown";
public Company(String name, String code, String mail) {
this.name = name;
this.code = code;
this.mail = mail;
}
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", code='" + code + '\'' +
", mail='" + mail + '\'' +
'}';
}
}
// 网络
class NetworkType {
// 运营商
private Company isp;
private String type;
@Override
public String toString() {
return "NetworkType{" +
"isp=" + isp +
", type='" + type + '\'' +
'}';
}
public NetworkType(Company isp, String type) {
this.isp = isp;
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
class Color {
public Color(int red, int green, int blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
// 默认白色
private int red = 255;
private int green = 255;
private int blue = 255;
}
// 生成器接口 --调用端依赖接口
abstract class CellBuilder {
// 多态返回实际手机星系
abstract CellPhone build();
// 构建厂商
abstract CellBuilder buildCompany(String name, String code, String mail);
abstract CellBuilder buildColor(int red, int green, int blue);
abstract CellBuilder buildNetwork(String ispName, String ispCode, String ispMail, String type);
abstract CellBuilder buildWeight(int weight);
abstract CellBuilder buildPrice(double price);
}
// 设置华为手机的默认属性
class HuaWeiCellPhoneBuilder extends CellBuilder{
// 生成器需要与实际产品进行绑定-多态
private CellPhone cellPhone = new HuaWeiCellPhone();
@Override
CellPhone build() {
return cellPhone;
}
@Override
CellBuilder buildCompany(String name, String code, String mail) {
Company company = new Company(name, code, mail);
cellPhone.setManufacturer(company);
return this;
}
@Override
CellBuilder buildColor(int red, int green, int blue) {
cellPhone.setColor(new Color(red, green, blue));
return this;
}
@Override
CellBuilder buildNetwork(String ispName, String ispCode, String ispMail, String type) {
cellPhone.setNetworkType(new NetworkType(new Company(ispName, ispCode, ispMail), type));
return this;
}
@Override
CellBuilder buildWeight(int weight) {
cellPhone.setWeight(weight);
return this;
}
@Override
CellBuilder buildPrice(double price) {
cellPhone.setPrice(price);
return this;
}
}
// 构建器
class HonorCellPhoneDirect {
CellBuilder cellBuilder = null;
public HonorCellPhoneDirect(CellBuilder builder) {
this.cellBuilder = builder;
}
public void construct(String ipsName, String netWorkType) {
// 作为华为手机的生成器,通过填充默认值的形式,来初始化华为手机
cellBuilder.buildNetwork(ipsName, "100010", "mail.100010.cn", netWorkType)
.buildCompany("honor", "12000", "mail.honor.huawei.com")
.buildPrice(6666.66)
.buildWeight(10);
}
}
常用生成器模型
- 简单类图
2. 实现
实际使用生成器模式,往往省掉
Director
而直接由Builder
build实际需要产品对象,当然这取决于实际的业务场景。如果使用Direct
时那么就可以构建FtpDirector
等等模板,来实现简单传参和解构。
/** 使用生成器+单例模式构建url
* @Author: Shiraki
* @Date: 2020/9/17 20:24
*/
public class UrlBuilder {
private String protocol = "http";
private String host = "127.0.0.1";
private Integer port = 80;
private Map<String, String> params = null;
private String userName = "admin";
private String passWord = "123456";
private String path = "/index";
static private String paramDiv = "&";
// 阻止new
private UrlBuilder() {
}
public static UrlBuilder create() {
return new UrlBuilder();
}
public UrlBuilder buildProtocol(String protocol) {
this.protocol = protocol;
return this;
}
public UrlBuilder buildHost(String hostName) {
this.host = hostName;
return this;
}
public UrlBuilder buildPath(String path) {
this.path = path;
return this;
}
public UrlBuilder buildPort(Integer port) {
this.port = port;
return this;
}
public UrlBuilder buildPassWord(String passWord) {
try {
this.passWord = URLEncoder.encode(passWord, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return this;
}
public UrlBuilder builderParams(Map<String, String> params) {
this.params = params;
return this;
}
public String build() {
StringBuilder url = new StringBuilder();
url.append(protocol)
.append("://")
.append(userName).append(":")
.append(passWord).append("@")
.append(host).append(":").append(port)
.append(path);
if (null != params) {
this.params.forEach((k, v) -> url.append(k).append("=").append(v).append(paramDiv));
}
return url.substring(0, url.length() - 1);
}
public static void main(String[] args) {
// 单例创建构造器
UrlBuilder builder = UrlBuilder.create();
// 构建参数
builder.builderParams(Stream.of("key1", "key2").collect(Collectors.toMap(String::toString, String::toUpperCase)));
// 打印构建结果
System.out.println("builder.build() = " + builder.build());
}
}
总结
生成器的模型里编程实现的时候往往采用的是链式编程的编码方式。但是使用链式编程并不一定是生成器模型,链式编程只是一个便捷的手段。生成器模型也经常省略Direct
。
优点:
- 能够隐藏对象内部修改,因为使用生成器模式的时候只需要关注生成器的接口,无需关注实际对象的构造方法,同时与生成器关联的实现解耦。
- 提供对创建对象的精细控制,通俗一点说就是你可以一步一步填写或者选择对象的属性,当对象的属性满足你的需求后最后返回对象。
缺点
- 一个构建器只能对应一个产品,其实也不是缺点,因为生成器模式本身也不是用来适配多个产品的方式,是对单一复杂对象构建。如果要构建多个不同类型的对象,那么可以选择抽象工厂模式。