想重新温习一下设计模式,所以准备写到一个专栏来记录,这是第一篇。知识的学习应该通过输入-内化-输出三个过程,输出的过程尤为重要,写博客也是为了输出知识以巩固之。设计模式可以说是面向对象编程的秘笈,凝结了无数前人们的经验积累和实践探索,最终形成了这些针对某类特定问题的的高效、优雅的解决方案。是开发人员进阶必须掌握的内容。
设计模式的使用,如果在开发阶段遇到响应问题能立刻想到某种设计模式自然是最好不过。也可以在优化阶段,使用设计模式重构代码,使之变得更加优雅高效。
现在网上有无数的关于各种设计模式的内容供大家学习使用,虽然各教程中都会给出案例,但大多数都是黑板上的案例,缺少真实场景下的应用,或者已有的经典使用场景,例如在JDK中对某些设计模式的使用。我准备从一下几个方面对某个设计模式进行阐述:
简介
- 构建者模式是一种创建型模式,创建型模式(Creational Pattern)说直白些就是代替new关键字的作用。
- 构建者模式的定义是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
真实开发场景的问题引入
下面是一个web开发中接口请求的统一结果返回类:
public class Result {
//是否成功
private Boolean success = true;
//返回码
private Integer code = 200;
//返回消息
private String message = "请求成功";
//返回数据
private Map<String,Object> data = new HashMap<>();
//省略get、set
}
在每一个接口中都要去实例化一遍这个类,并且给属性赋值。当前我们有两种方式实例化并赋值:
- 使用含参构造函数:
我们需要定义多个构造参数来满足不同的需要,比如有时候我只需要给data赋值,有时候需要给data和code,有时候不用赋值而都是用默认值,还有时候会给四个属性全部重新赋值 - 使用无参构造函数+set方法:
每次都要使用给四个属性赋值。 - 可见现有的方案都比较麻烦,写起来不方便
构建者模式讲解
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
核心类及类图
- Director:指挥者,通过指挥者将建造者与产品分离
- Builder:抽象建造者,定义产品构造的规范
- ConcreteBuilder:真实建造者,建造产品的类
- Product:产品
基本代码
Product.java
:产品类
public class Product {
private List<String> items = new ArrayList<>();
public void addItem(String item){
items.add(item);
}
public void show(){
items.forEach(item->{
System.out.println(item+"\n");
});
}
}
Builder.java
:抽象构造着
public interface Builder {
public void buildPartA();
public void buildPartB();
public Product getResult();
}
ConcreteBuilderA.java
:具体的建造者A
public class ConcreteBuilderA implements Builder {
private Product product = new Product();
@Override
public void buildPartA() {
product.addItem("部件X");
}
@Override
public void buildPartB() {
product.addItem("部件Y");
}
@Override
public Product getResult() {
return product;
}
}
ConcreteBuilderB.java
:具体的建造者B
public class ConcreteBuilderA implements Builder {
private Product product = new Product();
@Override
public void buildPartA() {
product.addItem("部件A");
}
@Override
public void buildPartB() {
product.addItem("部件B");
}
@Override
public Product getResult() {
return product;
}
}
Director.java
:指导者
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct(){
builder.buildPartA();
builder.buildPartB();
}
}
Client.java
:客户端使用
public class Client {
public static void main(String[] args) {
Builder builder;
//创建产品A
builder= new ConcreteBuilderA();
Director director1 = new Director(builder);
director1.construct();
builder.getResult().show();
//创建产品B
builder = new ConcreteBuilderB();
Director director2 = new Director(builder);
director2.construct();
builder.getResult().show();
}
}
输出:
利用构建者模式解决问题
直接上代码,在解释:
public class Result {
//是否成功
private Boolean success = true;
//返回码
private Integer code = 200;
//返回消息
private String message = "请求成功";
//返回数据
private Map<String,Object> data = new HashMap<>();
private Result(Builder builder) {
this.success = builder.success;
this.code = builder.code;
this.message = builder.message;
this.data = builder.data;
}
public static class Builder{
private Boolean success = true;
private Integer code = 200;
private String message = "请求成功";
private Map<String,Object> data = new HashMap<>();
public Builder() {
}
public Builder ok(){
return this;
}
public Builder error(){
this.success = false;
this.code = 500;
return this;
}
public Builder success(Boolean success){
this.success = success;
return this;
}
public Builder code(Integer code){
this.code = code;
return this;
}
public Builder message(String message){
this.message = message;
return this;
}
public Builder data(Map<String,Object> data){
this.data = data;
return this;
}
public Builder data(String key,Object value){
this.data.put(key,value);
return this;
}
public Result build(){
return new Result(this);
}
}
@Override
public String toString() {
return "Result{" +
"success=" + success +
", code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
Client.java
:使用
public class Client {
public static void main(String[] args) {
Result result = new Result.Builder().ok().build();
System.out.println(result.toString());
}
}
输出:
解析:
- 和标准的构建者模式相比,Result就是产品,同时是指导者;内部类Builder就是具体的构建者。
- 在这并没有抽象的构造者。
- 构建者模式的目的是将类的构建和表示分离,使得同样的构建过程可以创建不同的表示。如上:类的构建实在Builder类中,表示那自然就是Reult了。同样的构造过程,不同的参数会创建不一样的实例。
构建者模式的应用实例----StringBuilder
在JDK中,凡是以XxxBuilder结尾的类基本都是使用了构建者模式,以StringBuilder举例
查看下面代码:
public class BuilderTest {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder("Hello World");
System.out.println(builder);
}
}
//输出:Hello World
查看StringBuilder源码:
发现 StringBuilder 继承了一个 AbstractStringBuilder 类,我们点开 AbstractStringBuilder 类看下,发现 AbstractStringBuilder 实现了一个 Appendable 接口,如下:
分析:
- Appendable为抽象构造者
- AbstractStringBuilder 为具体构造者,只不过没法实例化,所以实例化的任务交给了StringBuilder
- StringBuilder既充当了指挥者角色,同时也充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成,而 StringBuilder 继承了 AbstractStringBuilder
总结
- 设计模式的使用应该灵活,不要拘泥于基本实现代码的结构,重要的是搞清楚那个类担当哪个角色,一个类也有可能身兼多个角色
- 类实例是由构建者创建的,指挥者持有一个构建者从而指挥构建者创建实例对象,不同的构造者构造不同的实例对象。这样类的构建和表示就分开了,同样的过程,构造者不同,最终获得的对象实例也不同。这就解释了构建者模式的定义将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
解决的问题
主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
使用场景
一些基本部件不会变,而其组合经常变化的时候。比如如下实例:
我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal。
优缺点
优点:
1. 建造者独立,易扩展。
2. 便于控制细节风险。
缺点:
3. 产品必须有共同点,范围有限制。
4. 如内部变化复杂,会有很多的建造类。
参考:
如有不足欢迎指正!