【设计模式之美】【建造型】建造者模式:处理复杂成员变量以及它们之间的关系
创始人
2025-01-07 03:04:29
0

文章目录

  • 一. 使用场景
  • 二. 具体实现
  • 三. 小结
    • 1. 结合场景使用
    • 2. 与工厂模式的区别

建造者模式本身不难,重点是掌握好它的适用场景。

一. 使用场景

如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和易用性,我们可以通过构造函数配合 set() 方法来解决。

但是,如果存在下面情况中的任意一种,我们就要考虑使用建造者模式了。

  • 如果创建对象时必填的属性有很多,那构造函数就会出现参数列表很长的问题。此时我们也无法通过set方法设置必填属性,因为没有设置的必填属性无法判断。
  • 如果类的属性之间有一定的依赖关系或者约束条件,我们继续使用构造函数配合 set() 方法的设计思路,那这些依赖关系或约束条件的校验逻辑就无处安放了。
  • 如果我们希望创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值,此时我们就不能在类中暴露 set() 方法。

接下来看建造者模式是如何解决这些问题的。

 

二. 具体实现

对于校验与属性之间存在关系:

我们可以把校验逻辑放置到 Builder 类中,先创建建造者,并且通过 set() 方法设置建造者的变量值,然后在使用 build() 方法真正创建对象之前,做集中的校验,校验通过之后才会创建对象。

对于不可变对象:

把类的构造函数改为 private,这样就只能通过建造者来创建对象。另外不提供提供任何 set() 方法,这样创建出来的对象就是不可变对象了。

具体的代码如下所示:

public class ResourcePoolConfig {   private String name;   private int maxTotal;   private int maxIdle;   private int minIdle;    private ResourcePoolConfig(Builder builder) {     this.name = builder.name;     this.maxTotal = builder.maxTotal;     this.maxIdle = builder.maxIdle;     this.minIdle = builder.minIdle;   }   //...省略getter方法...    //我们将Builder类设计成了ResourcePoolConfig的内部类。   //我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。   public static class Builder {     private static final int DEFAULT_MAX_TOTAL = 8;     private static final int DEFAULT_MAX_IDLE = 8;     private static final int DEFAULT_MIN_IDLE = 0;      private String name;     private int maxTotal = DEFAULT_MAX_TOTAL;     private int maxIdle = DEFAULT_MAX_IDLE;     private int minIdle = DEFAULT_MIN_IDLE;      public ResourcePoolConfig build() {       // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等       if (StringUtils.isBlank(name)) {         throw new IllegalArgumentException("...");       }       if (maxIdle > maxTotal) {         throw new IllegalArgumentException("...");       }       if (minIdle > maxTotal || minIdle > maxIdle) {         throw new IllegalArgumentException("...");       }        return new ResourcePoolConfig(this);     }      public Builder setName(String name) {       if (StringUtils.isBlank(name)) {         throw new IllegalArgumentException("...");       }       this.name = name;       return this;     }      public Builder setMaxTotal(int maxTotal) {       if (maxTotal <= 0) {         throw new IllegalArgumentException("...");       }       this.maxTotal = maxTotal;       return this;     }      public Builder setMaxIdle(int maxIdle) {       if (maxIdle < 0) {         throw new IllegalArgumentException("...");       }       this.maxIdle = maxIdle;       return this;     }      public Builder setMinIdle(int minIdle) {       if (minIdle < 0) {         throw new IllegalArgumentException("...");       }       this.minIdle = minIdle;       return this;     }   } }    //通过内部类builder来创建对象,build的时候会校验属性并创建对象。 ResourcePoolConfig config = new ResourcePoolConfig.Builder()         .setName("dbconnectionpool")         .setMaxTotal(16)         .setMaxIdle(10)         .setMinIdle(12)         .build(); // 这段代码会抛出IllegalArgumentException,因为minIdle>maxIdle 

 

三. 小结

1. 结合场景使用

如果对象只是用来映射数据库读出来的数据,那我们直接暴露 set() 方法来设置类的成员变量值是完全没问题的。并且,使用建造者模式来构建对象,代码实际上是有点重复的,ResourcePoolConfig 类中的成员变量,要在 Builder 类中重新再定义一遍。

所以选择建造者模式,需要结合具体的实际场景来使用。

 

2. 与工厂模式的区别

  • 工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。
  • 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。

 

参考:
王争:《设计模式之美》

相关内容

热门资讯

一分钟内幕!科乐吉林麻将系统发... 一分钟内幕!科乐吉林麻将系统发牌规律,福建大玩家确实真的是有挂,技巧教程(有挂ai代打);所有人都在...
一分钟揭秘!微扑克辅助软件(透... 一分钟揭秘!微扑克辅助软件(透视辅助)确实是有挂(2024已更新)(哔哩哔哩);1、用户打开应用后不...
五分钟发现!广东雀神麻雀怎么赢... 五分钟发现!广东雀神麻雀怎么赢,朋朋棋牌都是是真的有挂,高科技教程(有挂方法)1、广东雀神麻雀怎么赢...
每日必看!人皇大厅吗(透明挂)... 每日必看!人皇大厅吗(透明挂)好像存在有挂(2026已更新)(哔哩哔哩);人皇大厅吗辅助器中分为三种...
重大科普!新华棋牌有挂吗(透视... 重大科普!新华棋牌有挂吗(透视)一直是有挂(2021已更新)(哔哩哔哩)1、完成新华棋牌有挂吗的残局...
二分钟内幕!微信小程序途游辅助... 二分钟内幕!微信小程序途游辅助器,掌中乐游戏中心其实存在有挂,微扑克教程(有挂规律)二分钟内幕!微信...
科技揭秘!jj斗地主系统控牌吗... 科技揭秘!jj斗地主系统控牌吗(透视)本来真的是有挂(2025已更新)(哔哩哔哩)1、科技揭秘!jj...
1分钟普及!哈灵麻将攻略小,微... 1分钟普及!哈灵麻将攻略小,微信小程序十三张好像存在有挂,规律教程(有挂技巧)哈灵麻将攻略小是一种具...
9分钟教程!科乐麻将有挂吗,传... 9分钟教程!科乐麻将有挂吗,传送屋高防版辅助(总是存在有挂)1、完成传送屋高防版辅助透视辅助安装,帮...
每日必看教程!兴动游戏辅助器下... 每日必看教程!兴动游戏辅助器下载(辅助)真是真的有挂(2025已更新)(哔哩哔哩)1、打开软件启动之...