设计模式之建造者模式(变种Builder模式)

xiaoxiao2021-02-28  19

问题产生背景:

最近在项目中使用了一个HTTP网络请求框架—OKHttp。在使用过程中,发现一段很有趣的代码如下:

HttpUrl url = new HttpUrl.Builder() .scheme("http") .host(host) .port(port) .encodedPath(uri) .build();

这种链式调用,可以使我们的代码看起来更加简洁易懂,这段代码最终是通过build来创建 HttpUrl对象,类似的还有: 我们在使用Gson将对象转为json时,当对象属性可能为null时,我们也需要转换,Gson实例化如下:

Gson gson = new GsonBuilder() .serializeNulls() .create();

上面两种创建对象的写法都是采用的builder模式,今天我们就来详细了解一下。

Builder模式:

一、基本介绍

建造者模式的定义为:将一个复杂对象的构建和它的表示分离开,使得同样的构建过程可以创建不同的表示。

建造者模式一共有4个角色:

1 . 抽象建造者(Builder)角色:该角色用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。

2 . 具体建造者(Concrete Builder)角色:该角色实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例。

3 . 产品(Product)角色:该角色是建造者中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的。

4 . 导演者(Director)角色:该角色负责安排已有模块的顺序,然后告诉Builder开始建造。

buidler模式的侧重点是在创建对象,也就是new对象方面。

二、常见的两种构建方式

1、多重载的构造函数 先举个例子:需求是这样 现在有个Person类,有name、age、location、job属性。这几个属性只有name是必须的,其余是可选的。ok,我们先来一个正常逻辑代码:

/** * @author cj34920 * Date: 2018/07/10 */ public class Person { private final String name; private int age; private String location; private String job; public Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.age = age; } public Person(String name, int age, String location) { this.name = name; this.age = age; this.location = location; } public Person(String name, int age, String location, String job) { this.name = name; this.age = age; this.location = location; this.job = job; } }

这种方式:简单! 真的很简单,要什么参数 构造函数一目了然。 但是,作为调用方,他在new对象的同时,要明白每个构造函数,否则一不小心把顺序填错了,岂不很尴尬。 而且 这是只有四个参数,如果10几个个怎么办,是不是要10几个构造函数? 缺点也很明确: 参数多了不适用,不容易维护。

2、setter方法构造:

/** * @author cj34920 * Date: 2018/07/10 */ public class Person { private String name; private int age; private String location; private String job; public String getName() { return name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } }

这种方式也是常见的构造方式。这种方式有一点比较好,容易理解,比如setName,就是给name赋值。 但是缺点也显而易见: 1 参数变量不是能final。 2 对象状态不连续,必须在4次setter之后才能获取一个完整状态的对象。 3 代码量偏多。同样的问题10个属性,我需要调用setXXX十几次。

用户可能拿到不完整对象

这里给大家解释一下什么是不完整状态的对象: 这里创建对象明显是2个步骤: 1、创建对象,2、赋值。 这种方式创建对象,就有可能在第二步赋值时,返回对象,那么创建的对象就是不完整的对象。

三、Builder模式如何调用

/** * @author cj34920 * Date: 2018/07/09 */ public class Person { private final String name; private int age; private String location; private String job; public String getName() { return name; } public int getAge() { return age; } public String getLocation() { return location; } public String getJob() { return job; } Person(PersonBuilder personBuilder) { this.name = personBuilder.name; this.location = personBuilder.location; this.job = personBuilder.job; this.age = personBuilder.age; } public static class PersonBuilder { final String name; int age; String location; String job; PersonBuilder(String userName) { this.name = userName; } public PersonBuilder age(int age) { this.age = age; return this; } public PersonBuilder location(String location) { this.location = location; return this; } public PersonBuilder job(String job) { this.job = job; return this; } public Person builder() { return new Person(this); } } }

调用方法:

Person person = new Person.PersonBuilder("cj") .age(24) .job("java") .location("苏州") .builder();

可以看到变种的builder模式包括以下内容:

1 一个静态内部类,静态内部类的参数和构建类一样。

2 外部类只提供get方法方便查看,静态内部类提供set方法,赋值操作。

3 静态内部类提供的setter操作,返回值是当前Builder本身。

4 外部类的构造参数是静态内部类,使用静态内部类的变量赋值给外部类。

5 最终提供builder返回外部类

这种builder模式跟传统的builder模式确实是不太一样。但其实本质还是一样的,我们可以一一对应:

产品(Product)角色::也就是创建一个类,声明其成员变量,相当与person类。抽象建造者角色:相当于静态内部类,复制产品定义的属性到静态内部类中,同时生成set方法。具体的建造者:也就是外部类提供的构造函数,将静态内部类的变量值赋值给外部类。导演角色:静态内部类中的builder方法。

优点: 看起来整齐。 先赋值后创建对象。 缺点: 需要编写额外的代码。

总结:

其实好多设计模式在平常代码中都有应用,只不过我们有些时候会选择忽略他们。 在学习过程中,我们应该以百分之百的好奇心去学习为什么要这样,你一定会有不一样的发现。

转载请注明原文地址: https://www.6miu.com/read-2800294.html

最新回复(0)