Java设计模式之Builder建造者模式学习

最近做了几个与消息推送相关的项目,主要是在某些时刻和场景下达到触发条件后,向用户或者是向系统推送不同消息内容。

鉴于不同场景下触发推送的内容和格式不同,而且为了避免每一个场景就建一个信息实体造成代码冗余。开发时就想建一个信息实体且尽可能的包含不同场景所需要的字段,这样实体不冗余了,但是实体里面的属性就会很多,而且因为不是每一个字段在具体场景中都会被用到。我们就需要针对不同的场景,创建很多参数个数或类型不一致的构造函数来应对不同的场景推送需求。

闲话不多说,样例代码贴上:

public class PushMsgEntity{
	
	protected InformEvent informEvent;
	
	protected SendMethod sendMethod;
	
	protected String eventType;
	
	protected long userId;
	
	protected String senderIp;
	
	protected PushMsgEntity() {}
	
	//场景1
	protected PushMsgEntity(InformEvent informEvent,long userId){
	    this.informEvent = informEvent;
	    this.userId = userId;
	}
	
	//场景2
	protected PushMsgEntity(InformEvent informEvent,long userId,SendMethod sendMethod){
	    this.informEvent = informEvent;
        this.userId = userId;
        this.sendMethod = sendMethod;
	}
	
	//场景...
	protected PushMsgEntity(InformEvent informEvent,long userId,SendMethod sendMethod,String senderIp,...){
	    this.informEvent = informEvent;
        this.userId = userId;
        this.sendMethod = sendMethod;
        this.senderIp = senderIp;
        ...
	}

	//省略其余的构造函数和set/get方法
	...
}

调用者需根据场景来通过不同的构造函数来实例化对象。
调用方式如下:

public static void main(String[] args) {
    	    //场景1实体信息
            PushMsgEntity entity1 = new PushMsgEntity(InformEvent.BIND_SUCCESS_REMINDER, 111L);
            //场景2实体信息
            PushMsgEntity entity2 = new PushMsgEntity(InformEvent.DELIVER_SUCCESS_REMINDER, 111L, SendMethod.SMS);
            //场景n...
            ...
}

这样有几点不好是:
1.类的作者不得不书写多种参数组合的构造函数,而且其中还需要设置默认参数值,这是一个需要细心而又枯燥的工作;
2.灵活性不高,以后每增加一种场景,有可能会需要增加一个构造函数来应对,可扩展性较低;
3.上述例子中未展示出来,如果构造函数中相邻的参数类型一致的话,调用者很容易会把参数内容赋值错误,这样在编译的时候不会报错,但运行结果是错误的。增加了BUG发生的几率。

鉴于上述场景需求,我们其实可以采用Builder建造者模式来解决。

Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成。所以上述场景使用Builder模式来替代多参数构造函数是一个比较好的实践法则。

下面贴出改造后的代码(部分代码):

public class PushMsgEntity{
    	
    	protected InformEvent informEvent;
    	
    	protected SendMethod sendMethod;
    	
    	protected String eventType;
    	
    	protected long userId;
    	
    	protected String senderIp;
    	
    	//区别点:构造函数的参数为建造者,在这里进行实体属性的赋值(对象组装)
    	protected PushMsgEntity(PushMsgEntityBuilder builder) {
            this.informEvent = pushMsgBuilder.informEvent;
			this.sendMethod = pushMsgBuilder.sendMethod;
			this.eventType = pushMsgBuilder.eventType;
			this.userId = pushMsgBuilder.userId;
			this.senderIp = pushMsgBuilder.senderIp;
        }
        //省略set/get方法和toString()方法
    	...

    	//Builder模式的表现方式,用另一个类(建造者Builder)去负责对象属性(部件)的组装(这里是内部类来实现)
    	public static class PushMsgEntityBuilder{
    	    protected InformEvent informEvent;
            protected SendMethod sendMethod;
            protected String eventType;
            protected long userId;
            protected String senderIp; 
            
            protected PushMsgEntityBuilder(){}
            
            //各个属性的组装(部件组装 )
            protected PushMsgEntityBuilder informEvent(InformEvent informEvent){
                this.informEvent = informEvent;
                return this;
            }
            
            public PushMsgEntityBuilder sendMethod(SendMethod sendMethod) {
                this.sendMethod = sendMethod;
                return this;
            }
            
            public PushMsgEntityBuilder eventType(String eventType) {
                this.eventType = eventType;
                return this;
            }
            
            public PushMsgEntityBuilder userId(long userId) {
                this.userId = userId;
                return this;
            }
            
            public PushMsgEntityBuilder senderIp(String senderIp){
                this.senderIp = senderIp;
                return this;
            }
            
            //最后由建造者(builder)返回整体对象信息(成品)
            public PushMsgEntity build(){
                return new PushMsgEntity(this);
            }
    	}
}

调用方式如下:

public static void main(String[] args) {
     PushMsgEntity msgEntity = new PushMsgEntityBuilder()
     .informEvent(InformEvent.INTERVIEW_1_DAY_BEFORE_REMINDER)
     .sendMethod(SendMethod.ANDROID_PUSH).userId(1L)
     .senderIp("111")
     .build();
 }

Builder模式和重叠构造器相比有几点好处:
1.赋值参数更清晰明了,不容易把参数值赋值错误;
2.对象的展示和创建分离开,同时也保证了对象的一致性,
3.相比重叠构造器,对调用者来说,代码阅读性也提高了很多。

这是我对Builder模式在实际需求应用中的一小点总结,记录下来,希望能对别人有所帮助,如有不对之处,欢迎拍砖指正。
 
        

暧昧贴