zoukankan      html  css  js  c++  java
  • 使用Builder模式创建复杂可选参数对象

    【本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究。若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!】

    在新建对象时,若需要对大量可选参数进行赋值,最常见的做法是JavaBeans模式,即调用一个无参构造方法创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。代码示例如下:

    public class Complex {
        private int size;
        private int color;
        private int range = 0;
        private int num = 0;
    
        public Complex(int size, int color) {
            this.size = size;
            this.color = color;
        }
    
        public void setRange(int range) {
            this.range = range;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    }
    

    这种模式需要的代码语句繁琐,而且这种做法阻止了把类做成不可变的可能。更加简洁的一种方式是通过多个构造方法去新建对象。比如下面的代码:

    public class Complex {
        private int size;
        private int color;
        private int range = 0;
        private int num = 0;
    
        public Complex(int size, int color) {
            this.size = size;
            this.color = color;
        }
    
        public Complex(int size, int color, int range) {
            this.size = size;
            this.color = color;
            this.range = range;
        }
    
        // public Complex(int size, int color, int num) {
        //     this.size = size;
        //     this.color = color;
        //     this.num = num;
        // }
    
        public Complex(int size, int color, int range, int num) {
            this.size = size;
            this.color = color;
            this.range = range;
            this.num = num;
        }
        
    }
    

    当参数越来越多时,这种方式就会使类的结构变得臃肿,难以维护,在使用时也需要去逐个理解每种构造方法的参数意义。并且在上述示例中,也是无法打开被注释掉的构造方法。

    有没有一种更好的创建大量可选参数对象的方式?答案是Builder模式。先来看下面的示例代码:

    public class NutritionFacts {
        private final int size;
        private final int color;
        private final int num;
        private final int range;
    
        public static class Builder {
            private int size;
            private int color;
            private int range = 0;
            private int num = 0;
    
            public Builder(int size, int color) {
                this.size = size;
                this.color = color;
            }
    
            public Builder num(int num) {
                this.num = num;
                return this;
            }
    
            public Builder range(int range) {
                this.range = range;
                return this;
            }
    
            public NutritionFacts build() {
                return new NutritionFacts(this);
            }
        }
    
        private NutritionFacts(Builder builder) {
            this.color = builder.color;
            this.num = builder.num;
            this.size = builder.size;
            this.range = builder.range;
        }
    
        public static void main(String[] args) {
            NutritionFacts build = new NutritionFacts.Builder(2, 1)
                    .num(3).range(4).build();
        }
    }
    

    通过观察示例代码,不难发现。这种模式的本质是不直接生成想要的对象,而是通过链式编程构建一个参数完备的构造器对象,最终通过调用构造器对象的build()方法来生成不可变的目标对象。

    显而易见,链式编程的秘诀在于从实例化对象开始,每次调用成员方法都会返回自身对象。

    上述示例略微繁琐,若是追求简便,似乎有更好的另一种写法,示例代码如下:

    
    public class NutritionFacts {
        private int size;
        private int color;
        private int num;
        private int range;
    
        public static class Builder {
            private NutritionFacts nutritionFacts = new NutritionFacts();
    
            public Builder(int size, int color) {
                this.nutritionFacts.size = size;
                this.nutritionFacts.color = color;
            }
    
            public Builder num(int num) {
                this.nutritionFacts.num = num;
                return this;
            }
    
            public Builder range(int range) {
                this.nutritionFacts.range = range;
                return this;
            }
    
            public NutritionFacts build() {
                return nutritionFacts;
            }
        }
    
        public static void main(String[] args) {
            NutritionFacts facts = new NutritionFacts.Builder(2, 1)
                    .num(3).range(4).build();
        }
    }
    

    写到这里,笔者打算扩展下内部类与静态内部类的相关知识。

    内部类与静态内部类

    【本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究。若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!】

    观察下面的静态内部类的声明,static关键字位于class关键字之前(顺序颠倒会报错),说明static的规则优先于class的规则。理解了这一点,就不难理解如何使用静态内部类。

    public class InnerClass {
    
        public class Inner{
            public Inner(){
                //..
            }
        }
    
        public static class StaticInner{
            static int i = 10;
            public StaticInner(){
                //..
            }
        }
    
    }
    
    

    对于内部类的实例化:

    因为内部类属于外部类的非静态成员,所以首先需要实例化外部类,其次因为内部类也属于类,使用之前故也需要实例化。

    public static void main(String[] args) {
    
        new InnerClass().new Inner();
    
    }
    

    对于静态内部类的实例化:

    因为static关键字必须声明在class之前,所以可知static的规则优先于class的规则。在处理类的静态成员时,优先使用“类名.静态成员”的形式,其次,再考虑实例化或其它变量引用。

    public static void main(String[] args) {
    
        new InnerClass.StaticInner();
    
        int i = InnerClass.StaticInner.i;
    }
    
    

    本文参考资料:《Effective Java》

    版权声明

    【本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究。若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!】

  • 相关阅读:
    科学计算和可视化
    利用Python制作GIF图片
    模拟体育竞技分析
    词云(傲慢与偏见)
    词频统计+词云(傲慢与偏见)
    汉诺塔问题
    Python 的turtle笔记
    有进度条的圆周率计算
    Python 第二周练习
    warning: deprecated conversion from string constant to ‘char*’
  • 原文地址:https://www.cnblogs.com/onblog/p/13036084.html
Copyright © 2011-2022 走看看