构造器模式已在“四个组成部分”的“设计模式”一章中进行了描述。 书:
构建器模式是一种设计模式,它允许使用正确的操作顺序逐步创建复杂的对象。 构造由导向对象控制,该导向对象仅需要知道要创建的对象的类型。
使用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;
}
}
这是一个巨大的改进,因为现在开发人员可以在编译时知道其构建对象无效。
下一步是想象更复杂的用例:
- 必须按一定顺序调用Builder方法。 例如,房屋应具有地基,框架和屋顶。 建造框架需要具有基础,而建造屋顶则需要框架。
- 更复杂的是,某些步骤取决于先前的步骤( 例如 ,只有在混凝土框架下才可以使用平屋顶)
该练习留给有兴趣的读者。 欢迎在评论中链接到建议的实施。
我们的设计存在一个缺陷:仅调用setLastName()方法就足以使我们的构建器有效,因此传递null会破坏我们的设计目的。 对于我们的编译时策略,在运行时检查null值还不够。 Scala语言功能可以利用这种设计的增强功能,即类型安全构建器模式。
摘要
- 在现实生活中的软件中,构建器模式不像在这里和那里找到的快速示例那样容易实现
- 少即是多:创建易于使用的DSL(非常)困难
- Scala使复杂的构建器实现的设计人员比Java更容易
翻译自: https://blog.frankel.ch/a-dive-into-the-builder-pattern/