设计模式之建造者模式---Builder Pattern

本文介绍了建造者模式的原理,如何通过构造函数和setter方法创建对象,以及在资源池配置场景下的应用。同时,对比了建造者模式与工厂模式的区别,指出前者用于创建同一类型但定制化的对象,而后者用于创建不同类型的相关对象。
摘要由CSDN通过智能技术生成

        建造者模式也称为构建者模式或者生成器模式。在看这篇文章之前可以思考思考两个问题:直接使用构造函数或者配合setter方法就能创建对象,为什么还需要使用建造者模式呢?构建者模式和工厂模式都可以创建对象,他们又会有什么区别呢?在看完这篇文章你想必就会有答案了。

        在平时的开发中,创建一个对象的常用方式就是使用new关键字调用类的构造函数来完成,假设有这样一道代码设计方面的面试题:编写代码实现一个资源配置类ResourcePoolConfig,这里的资源配置池可以简单地被理解为线程池、连接池和对象池等。假设这个配置类中有name、maxTotal、maxIdle、minIdle等成员变量。其中name是必填选项,且没有默认值,其他的非必填有默认值。对于这种问题,有一些开发经验的很轻松就能实现这个资源配置类。比较容易想到的是下面这种实现方式;

public class ResourcePoolConfig{
    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(String name , Integer maxTotal , Integer maxIdle , Integer minIdle){
        
        if(StringUtils.isBlank(name))
            throw new IllegalArgumentException("name should not be empty.");
        this.name = name;
        if(maxTotal != null){
            if(maxTotal <= 0){
                throw new  IllegalArgumentException("maxIdle should not be negative.");
            }
        }
        
        this.maxTotal = maxTotal;
        if(maxIdle != null){
            if(maxIdle <= 0){
                throw new  IllegalArgumentException("maxIdle should not be negative.");
            }
        }
        
        this.maxIdle = maxIdle;
        if(minIdle != null){
            if(minIdle <= 0){
                throw new  IllegalArgumentException("maxIdle should not be negative.");
            }
        }
        
        this.minIdle = minIdle;
    }

    //省去getter方法
}
    

        对于当前这个例子只有4个配置项,对应到构造函数并不多。但是当配置项逐渐变多的时候,按照上述的设计思路,构造函数的列表就会变得很长,代码的可读性和易用性就会变得很差。在使用构造函数的时候也可能出错。

        既然上面提到了,参数太多使用太麻烦,我们解决上面的方式也很简单,我们使用setter来替代构造函数为成员变量赋值。

public class ResourcePoolConfig{
    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(String name , Integer maxTotal , Integer maxIdle , Integer minIdle){
        
        if(StringUtils.isBlank(name))
            throw new IllegalArgumentException("name should not be empty.");
        this.name = name;
        
    }

    public void setMaxTotal(int maxTotal){
           if(maxTotal <= 0)
              throw new  IllegalArgumentException("maxTotal should not be negative.");        
            this.maxTotal = maxTotal;
    }
    
    public void setMaxIdle(int maxIdle){
         if(maxIdle <= 0)
              throw new  IllegalArgumentException("maxIdle should not be negative.");        
            this.maxIdle = maxIdle;
    }
     public void setMinIdle(int minIdle){
         if(minIdle <= 0)
              throw new  IllegalArgumentException("minIdle should not be negative.");        
            this.minIdle = minIdle;
    }
    //省去getter方法
}
    

        重构之后的代码可读性和易用性就提高了很多。

使用建造者模式做参数校验

        从始至终我们都没用到建造者模式,只是使用了构造函数和setter方法设置可选项,即可满足需求。但是如果我们加大难度,就不一定了。比如还需要满足以下三个要求呢?

  1.   name是必填项
  2. 如果设置了其他三个成员变量中的一个就要显示的设置其他的,又或者maxIdle,minIdle都要小于maxTotal
  3. 我们希望配置类对象是不可变对象,创建好了之后就不能修改内部的值

为了解决这几个问题那么建造者模式就登场了,我们利用建造者模式对代码进行重构。

public class ResourcePoolConfig{
    
        
    private String name;
    private int maxTotal;
    private int maxIdle;
    private int minIdle;
    
    public ResourcePoolConfig(Builder builder){
        
        this.name = builder.name;
        this.maxTotal = builder.maxTotal;
        this.minIdle = builder.minIdle;
        this.maxIdle = builder.maxIdle;
        
    }

    //省去getter方法
    
    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 || minIdle > maxTotal)
                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 setMinIdle(int minIdle){
            if(maxIdle <= 0)
                throw new IllegalArgumentException("....");
            this.minIdle = minIdle;
            return this;
        }

        public Builder setMaxIdle(int MaxIdle){
            if(maxIdle <= 0)
                throw new IllegalArgumentException("....");
            this.maxIdle = maxIdle;
            return this;
        }
    }
}
  

这样设计代码就能满足上面的需求,并且能够避免对象处于一个无效状态。

建造者模式与工厂模式的区别

        建造者模式和工厂模式都可以用来创建对象,二者的区别是啥呢?

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值