zoukankan      html  css  js  c++  java
  • java的设计模式

    Builder 模式的目的?

    构造对象的方式过于复杂,不如将之抽离出来。比如,构造器参数过多
    这样说也有点抽象,举个例子吧。

    举个例子

    比如 非常热门的消息队列RabbitMQAMQP.BasicProperties

    因为它的属性比较多,所以构造函数也是挺吓人的。

    我看到也不太想调用。
    如果现在要构造一条消息

    • 投递模式(delivery mode)为 2
    • 优先级(priority)是 2
    • content-type 为 text/plain

    在没有 builder 模式之前,是这样构造的

    new AMQP.BasicProperties("text/plain",null,null,2,1,null,null,null,null,null,null,null,null,null);
    

    痛苦啊!!!不信,你自己也可以尝试构造一下。

    • 构造函数有很多你不想设置的参数
    • 你要看准,哪个参数要赋值,哪个参数不赋值,一不小心就可能出错了。而这里有 14 个参数。。。
    • 维护性差,写完代码再看一下,也看不出这个参数究竟是什么意思。还要点进去,一个一个参数地看才知道是什么意思

    而用了 Builder 模式后。

    new AMQP.BasicProperties.Builder()
        .contentType("text/plain")
        .deliveryMode(2)
        .priority(1)
        .build();
    

    舒畅!!!

    Builder 是如何实现?

    很简单。

    • BasicProperties中添加一个叫Builder的内部类
    • Builder 中所有字段和BasicProperties类是完全一致的
    • Builder实例在调用build函数的时候,再调用BasicProperties的构造函数构造对象。

    代码如下

    public static class BasicProperties{
        private String contentType;
        private String contentEncoding;
        private Map<String,Object> headers;
        private Integer deliveryMode;
        private Integer priority;
        //... 还有很多属性
    
        public BasicProperties(
            String contentType,
            String contentEncoding,
            Map<String,Object> headers,
            Integer deliveryMode,
            //...
            String clusterId){
                this.contentType = contentTypel;
                this.contentEncoding = contentEncoding;
                //...
        }
    
        public static final class Builder {
            private String contentEncoding;
            private Map<String,Object> headers;
            private Integer deliveryMode;
            private Integer priority;
            //.. 和BasicProperties的字段一致的。
            
            public Builder contentType(String contentType){
                this.contentType = contentType; 
                return this; 
            }
        
            public Builder contentEncoding(String contentEncoding){
                this.contentEncoding = contentEncoding; 
                return this; 
            }
            
            public BasicProperties build() {
                return new BasicProperties
                    ( contentType
                      contentEncoding,
                      //还有很多属性
                    );
            }
        }
    }
    

    分析

    Builder 模式的好处

    • 不用花太多心思去记构造器的顺序,在 ide 中输入一个点就有自动提示了
    • 好维护,很容易看到看明白这是什么属性

    坏处

    • 构造对象就要先调用 Buidler 构造器,多了构造器的开销
    • 类的关系变得复杂了

    其他的做法

    如果不用 Builder 模式,有其他的做法吗?

    重叠构造器?

    比如,上面的例子,我构造的消息只需 投递模式(delivery mode)、优先级(priority)、 content-type ,专门为这几个参数弄个专门的构造函数,可以吗?
    调用就变成这样了。

    new AMQP.BasicProperties("text/plain",2,1)
    

    可以,

    • 但依然不太好看。
    • 如果有不同的需求,各种属性都排列组合一下也麻烦。
    • 不实际,因为类字段的类型可能会是一样的,有些组合注定不行

    javaBean 模式呢?

    BasicProperties  p = new AMQP.BasicProperties();
    p.setContentType("text/plain");
    p.setDeliveryMode(2);
    p.setPriority(1);
    

    在《effective java》中就探讨过这个可能,书中是这样说的

    因为构造过程被分到几个调用中,在构造过程中 javaBean 可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将导致失败,这种失败与包含错误的代码大相径庭,因此它调试起来十分困难。与此相关的另一点不足在于,javaBeans 模式阻止了把类做成不可变的可能,这需要程序员付出格外的努力来确保它的线程安全。

    这话就有点摸不着头脑,什么意思

    其实意思是大概的,

    1. javaBean 是构造与字段赋值分离的,有可能 线程 1 在给对象 Obj 赋值,还没有赋完成的时候,线程 2 就拿了 Obj 的值了,就不一致了
    2. 如果 Obj 的字段全都是 final 的,不会出现上面那种情况,但字段只能会通过构造函数赋值(builder 模式也行),不能使用 javaBeans 的 setXXX 函数赋值了。
    3. 对多线程要求的,比如是传给消息队列的对象,程序员要保证下线程安全。
    4. 这也是个一个开放开闭的问题,Javabean 这样的写法确实和完全开放没啥区别,如果字段确定下来不用改了就最好设为 final 。

    以上

  • 相关阅读:
    理解SynchronizationContext,如何在Winform里面跨线程访问UI控件
    ThreadPool.QueueUserWorkItem引发的血案,线程池异步非正确姿势导致程序闪退的问题
    实战经验分享之C#对象XML序列化
    C#wxpay和alipay
    C#调用windows api 实现打印机控制
    C#winform程序关闭计算机的正确姿势
    自动化控制之线程池的使用
    自动化控制之重码校验
    (转)C#中的那些全局异常捕获
    android studio 2.32躺坑记
  • 原文地址:https://www.cnblogs.com/jojo-feed/p/10155214.html
Copyright © 2011-2022 走看看