4种Java类多参数构建模式

最近在实际项目中遇到了多参数初始化问题。如何在模块初始化时传入多个参数(5个以上)?如何更安全?如何更灵活?如何可扩展?如何简化调用代码?如何增强可读性?我在面试中也常提出这样的问题。以上内容如果读过《Efective Jave》会很容易在第二章找到答案。

《Efective Jave》中主要介绍了 3 种多参数构建方法,我在项目里还用到了第 4 种。下面一一介绍。

0x00 简单、常用:重叠构造器

代码示例:

public class A {
    private int v1;
    private int v2;
    private int v3;
    private int v4;
    private int v5;
    
    public A(int v1) { this(v1, DEFUALT_V2)}
    public A(int v1, int v2) { this(v1, v2, DEFUALT_V3)}
    public A(int v1, int v2, int v3) { this(v1, v2, v3, DEFUALT_V4)}
    public A(int v1, int v2, int v3, int v4) { this(v1, v2, v3, v4, DEFUALT_V5)}
    public A(int v1, int v2, int v3, int v4, int v5) {
        this.v1 = v1;
        this.v2 = v2;
        this.v3 = v3;
        this.v4 = v4;
        this.v5 = v5;
    }
}

优点:

  1. 逻辑、层级清晰明了。适合参数层级关系明确的类,如 Android 里的 View。

缺点:

  1. 参数增多时,可读性变差
  2. 如示例所示,如果连续传入多个同样类型的变量,容易把顺序弄错
  3. 灵活性不足,无法指定初始化特定几个参数

0x01 简单、灵活:重复 set 方法

代码示例:

public class B {
    private int v1 = DEFAULT_V1;
    private int v2 = DEFAULT_V2;
    private int v3 = DEFAULT_V3;
    private int v4 = DEFAULT_V4;
    private int v5 = DEFAULT_V5;
    
    public B() {}
    public void setV1(int val) { v1 = val; }
    public void setV2(int val) { v2 = val; }
    public void setV3(int val) { v3 = val; }
    public void setV4(int val) { v4 = val; }
    public void setV5(int val) { v5 = val; }
}

优点:

  1. 简单
  2. 灵活,支持定制式初始化,即参数可选,如果不需要自定义的变量可以直接不给初始化方法
  3. 支持动态改变参数,set 方法也可以在程序运行的任何阶段调用

缺点:

  1. 如果变量的需要按一定顺序初始化,那么这种调用方式将带来风险。实际项目被这个坑过。提供外部的接口最好在编写时排除这种漏洞的隐患
  2. set 方法可在任何时间调用,更改变量的值。和缺点 1 同理,如果变量耦合了其他逻辑,那么随时可调用 set 方法就是不安全的。

0x02 安全、可读:Builder 构建器

代码示例

public class C {
    private int v1;
    private int v2;
    private int v3;
    private int v4;
    private int v5;
    
    public static class Builder {
        private int v1 = DEFAULT_V1;
        private int v2 = DEFAULT_V2;
        private int v3 = DEFAULT_V3;
        private int v4 = DEFAULT_V4;
        private int v5 = DEFAULT_V5;

        public Builder(int v1) { this.v1 = v1; return this;}
        public Builder v2(int val) { this.v2 = val; return this; }
        public Builder v3(int val) { this.v3 = val; return this; }
        public Builder v4(int val) { this.v4 = val; return this; }
        public Builder v5(int val) { this.v5 = val; return this; }
        public C build() { return new C(this); }
    }

    private C(Builder builder) {
        this.v1 = builder.v1;
        this.v2 = builder.v2;
        this.v3 = builder.v3;
        this.v4 = builder.v4;
        this.v5 = builder.v5;
    }
}
// Usage:
// C c = new C.Builder(1).v2(2).v3(3).v4(4).v5(5).build();

优点:

  1. 灵活,支持定制式初始化
  2. 可读性强,参见 Android 的 Animator 类,一个语句完成类的初始化和调用
  3. 可以在 build() 方法内统一进行参数校验,为《Ecfective Java》推荐方法

缺点:

  1. 复杂,构建所需代码行数增加
  2. 参数在传入时为确定参数,不支持动态改变

0x03 实时参数:Provider 模式

该方法借鉴了 Builder 构建器,但是将构建器独立于类外,同时提供实时获取的接口。

public class D {
    private int v1;
    private int v2;
    private int v3;
    private int v4;
    private int v5;
    
    public interface Provider {
        public int getV1();
        public int getV2();
        public int getV3();
        public int getV4();
        public int getV5();
    }

    public D(Provider provider) {
        this.v1 = provider.getV1();
        this.v2 = builder.v2;
        this.v3 = builder.v3;
        this.v4 = builder.v4;
        this.v5 = builder.v5;
    }
}
// Usage:
// public class IProvider implement D.Provider {
//        @Override
//        public int getV1() { return val; }
//        @Override
//        public int getV2() { return val; }
//        @Override
//        public int getV3() { return val; }
//        @Override
//        public int getV4() { return val; }
//        @Override
//        public int getV5() { return val; } 
// }
// D d = new D(new IProvider());

优点:

  1. 借鉴了 Builder 构建器的优点,隔离了参数传入方法和初始化方法
  2. Provider 由调用方实现,使动态参数的传入变为可能
  3. 调用节点只需要调用一行语句,初始化参数的准备移至他处。这种场景需求可能发生在一个核心类的 initAll() 方法中,调用者需要在该方法内先后初始化十几个模块。如果每个模块都要写十行代码那就非常恐怖了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,192评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,858评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,517评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,148评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,162评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,905评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,537评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,439评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,956评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,083评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,218评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,899评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,565评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,093评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,201评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,539评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,215评论 2 358

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,676评论 18 139
  • 对象的创建与销毁 Item 1: 使用static工厂方法,而不是构造函数创建对象:仅仅是创建对象的方法,并非Fa...
    孙小磊阅读 1,986评论 0 3
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,220评论 25 707
  • 你是夏日冰凉汽水里咕咚咚的气泡,说穿了也没什么味道,却是所有的清凉与畅快; 你是夜路上一缕微醺的晚风,驱不开夜色照...
    吉莉安阅读 286评论 0 1
  • 前面我们讲了,在进攻端大致有四种状态或者阶段:快攻阶段、过渡阶段、阵地进攻阶段、不打战术阶段(二次进攻、对方主动性...
    篮球伊甸园阅读 334评论 0 0