深入了解Builder模式

构造器模式已在“四个组成部分”的“设计模式”一章中进行了描述。 书:

构建器模式是一种设计模式,它允许使用正确的操作顺序逐步创建复杂的对象。 构造由导向对象控制,该导向对象仅需要知道要创建的对象的类型。

生成器类图

使用Builder模式的常见实现是拥有一个流畅的界面,并带有以下调用者代码:

Personperson=newPersonBuilder().withFirstName("John").withLastName("Doe").withTitle(Title.MR).build();

以下构建器可以启用此代码段:

publicclassPersonBuilder{

    privatePersonperson=newPerson();
    publicPersonBuilderwithFirstName(StringfirstName){
        person.setFirstName(firstName);
        returnthis;
    }

    // Other methods along the same model
    // ...

    publicPersonbuild(){
        returnperson;
    }
}

构建器的工作得以实现: Person实例得到了很好的封装,只有build()方法最终返回了构建的实例。 通常这是大多数文章停止的地方,假装已经涵盖了该主题。 不幸的是,可能会出现一些需要更深入工作的情况。

假设我们需要一些验证来处理最终的Person实例, 例如 lastName属性是必需的。 为此,我们可以轻松地检查build()方法中的属性是否为null并相应地引发异常。

publicPersonbuild(){

    if(lastName==null){
        thrownewIllegalStateException("Last name cannot be null");
    }

    returnperson;
}

当然,这解决了我们的问题。 不幸的是,这种检查是在运行时进行的 ,因为调用我们代码的开发人员会发现(这极大地困扰了他们)。 为了实现真正的DSL,我们必须进行很多设计更新。 我们应该执行以下调用者代码:

Personperson1=newPersonBuilder().withFirstName("John").withLastName("Doe").withTitle(Title.MR).build();// OK
Personperson2=newPersonBuilder().withFirstName("John").withTitle(Title.MR).build();// Doesn't compile

我们必须更新我们的生成器,它可能要么返回自身, 缺乏一个无效的建设者build()方法,在下面的图表。 请注意,第一个PersonBuilder类被保留,因为调用代码的入口点不需要,如果不需要的话,则不必应对Valid- / InvaliPersonBuilder

示例构建器状态图

这可能会转换为以下代码:

publicclassPersonBuilder{

    privatePersonperson=newPerson();

    publicInvalidPersonBuilderwithFirstName(StringfirstName){
        person.setFirstName(firstName);
        returnnewInvalidPersonBuilder(person);
    }

    publicValidPersonBuilderwithLastName(StringlastName){
        person.setLastName(lastName);
        returnnewValidPersonBuilder(person);
    }

    // Other methods, but NO build() methods
}

publicclassInvalidPersonBuilder{

    privatePersonperson;

    publicInvalidPersonBuilder(Personperson){
        this.person=person;
    }

    publicInvalidPersonBuilderwithFirstName(StringfirstName){
        person.setFirstName(firstName);
        returnthis;
    }

    publicValidPersonBuilderwithLastName(StringlastName){
        person.setLastName(lastName);
        returnnewValidPersonBuilder(person);
    }

    // Other methods, but NO build() methods
}

publicclassValidPersonBuilder{

    privatePersonperson;

    publicValidPersonBuilder(Personperson){
        this.person=person;
    }

    publicValidPersonBuilderwithFirstName(StringfirstName){
        person.setFirstName(firstName);
        returnthis;
    }

    // Other methods

    // Look, ma! I can build
    publicPersonbuild(){
        returnperson;
    }
}

这是一个巨大的改进,因为现在开发人员可以在编译时知道其构建对象无效。

下一步是想象更复杂的用例:

  1. 必须按一定顺序调用Builder方法。 例如,房屋应具有地基,框架和屋顶。 建造框架需要具有基础,而建造屋顶则需要框架。
  2. 更复杂的是,某些步骤取决于先前的步骤( 例如 ,只有在混凝土框架下才可以使用平屋顶)

该练习留给有兴趣的读者。 欢迎在评论中链接到建议的实施。

我们的设计存在一个缺陷:仅调用setLastName()方法就足以使我们的构建器有效,因此传递null会破坏我们的设计目的。 对于我们的编译时策略,在运行时检查null值还不够。 Scala语言功能可以利用这种设计的增强功能,即类型安全构建器模式。

摘要

  1. 在现实生活中的软件中,构建器模式不像在这里和那里找到的快速示例那样容易实现
  2. 少即是多:创建易于使用的DSL(非常)困难
  3. Scala使复杂的构建器实现的设计人员比Java更容易

翻译自: https://blog.frankel.ch/a-dive-into-the-builder-pattern/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值