Foo foo = Foo.builder()
.setColor(red)
.setName("Fred")
.setSize(42)
.build();
所以我知道在调用方法时有以下“Builder”解决方案用于创建命名参数。虽然,这似乎只适用于内部静态类作为构建器,或者我错了吗?我看了一些构建器模式的教程,但是对于我想要做的事情,它们看起来非常复杂。是否有任何方法可以保持Foo类和Builder类分离,同时享受上述代码等命名参数的好处?
典型设置下方:
public class Foo {
public static class Builder {
public Foo build() {
return new Foo(this);
}
public Builder setSize(int size) {
this.size = size;
return this;
}
public Builder setColor(Color color) {
this.color = color;
return this;
}
public Builder setName(String name) {
this.name = name;
return this;
}
// you can set defaults for these here
private int size;
private Color color;
private String name;
}
public static Builder builder() {
return new Builder();
}
private Foo(Builder builder) {
size = builder.size;
color = builder.color;
name = builder.name;
}
private final int size;
private final Color color;
private final String name;
}
答案 0 :(得分:4)
您可以确定将Builder类的字段更改为私有 - 然后您只需要为每个"属性"提供一个(公共)getter方法。在建设者;并且Foo中的构造函数调用这些方法;而不只是获取Builder对象中的字段。
然后你可以将你的Builder类移出Foo。简单明了。
但请记住:最后,Builder和Foo密切相关。他们按设计共享常用字段集。所以对Foo的任何改变都会影响Builder;反之亦然。因此,让它们保持紧密联系是非常有意义的。也许不是内部/外部类,但可能仍然在同一个源文件中!但那时......其中只有一个可以公开。这真的是你想要的吗?!
换句话说:不要把事情分开,因为你可以"。只有在你有充分理由这样做的情况下才能做到这一点,并且如果出现这种情况更好而不是你当前的解决方案!
编辑:您的问题可能不是Foo和Builder的分离,而是您的Foo类首先包含太多字段。不要忘记单一的责任原则......当你的班级需要超过5,6个领域......它可能做得太多而且应该进一步切片!请记住:良好的OO设计首先是关于行为;不是在某个对象中有10个,20个字段!
答案 1 :(得分:2)
很难严格定义“ Builder Pattern™”,并且设计选择有几个自由度。有些概念很容易被混合或滥用,除此之外,说“你总是必须完全像那样”一般很难(而且几乎总是错误的) 。
问题是应用“模式”应该实现什么。在您的问题和示例中,您已经混合了两个概念,即builder pattern和fluent interface。扮演魔鬼的拥护者,甚至可以偷偷地说,在你的案例中,“建造者”只是托马斯已经提到过的Parameter Object,它是以一种特殊的方式(流利地)构建的,并且富含了{{1}的一些棘手组合。 }}和public
可见性。
构建器模式的一些可能目标是重叠或齐头并进。但你应该问问自己,你的主要目标是什么:
private
个最终字段,还是可能还有不应该公开的setter? (建造者仍然可以称这些非公共制定者!)因为所有这些问题都会对设计中的微妙差异产生影响。但是,关于你的实际的,高级别的“句法”问题:
您可以将构建器设计为final
内部类(您在示例中所做的)。
public static
这提供了最严格的隐私形式:public class Person {
...
public static PersonBuilder create() { ... }
public static class PersonBuilder {
...
public Person build() { ... }
}
}
和Person
的构造函数可能都是PersonBuilder
。
您还可以将实际的类及其构建器放在单独的文件中:
private
和
public class Person {
...
}
这里可以实现合理程度的隐私:两者的构造函数可以是包私有(即具有默认可见性)。
在这两种情况下,客户端的实际使用情况都是相同的,但构建器类的名称除外(public class PersonBuilder {
...
}
与package.Person.PersonBuilder
)。课程的“内容”也是相同的(除了略微不同的可见性)。在这两种情况下,您可以根据需要创建package.PersonBuilder
的子类,具体取决于构建器配置,构建器本身可以具有流畅的界面。
答案 2 :(得分:1)
作为构建器模式的替代方法,您还可以使用parameter object:
class FooParams {
public int size;
public Color color;
public String name;
}
如果您愿意,可以在此处使用getter和setter,而不是公共字段。
然后Foo
构造函数将其中一个作为参数:
public Foo(FooParams params) {
this.size = params.size;
this.color = params.color;
this.name = params.name;
}
答案 3 :(得分:0)
使用构图。为了使事情更简单,更清晰,请不要复制源(Foo
)和构建器(Builder
)类中的所有属性。
例如,Foo
内有Builder
个课程,而不是每个Foo
属性。
简单的代码段:
import java.util.*;
class UserBasicInfo{
String nickName;
String birthDate;
String gender;
public UserBasicInfo(String name,String date,String gender){
this.nickName = name;
this.birthDate = date;
this.gender = gender;
}
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("Name:DOB:Gender:").append(nickName).append(":").append(birthDate).append(":").
append(gender);
return sb.toString();
}
}
class ContactInfo{
String eMail;
String mobileHome;
String mobileWork;
public ContactInfo(String mail, String homeNo, String mobileOff){
this.eMail = mail;
this.mobileHome = homeNo;
this.mobileWork = mobileOff;
}
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("email:mobile(H):mobile(W):").append(eMail).append(":").append(mobileHome).append(":").append(mobileWork);
return sb.toString();
}
}
class FaceBookUser {
String userName;
UserBasicInfo userInfo;
ContactInfo contactInfo;
public FaceBookUser(String uName){
this.userName = uName;
}
public void setUserBasicInfo(UserBasicInfo info){
this.userInfo = info;
}
public void setContactInfo(ContactInfo info){
this.contactInfo = info;
}
public String getUserName(){
return userName;
}
public UserBasicInfo getUserBasicInfo(){
return userInfo;
}
public ContactInfo getContactInfo(){
return contactInfo;
}
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("|User|").append(userName).append("|UserInfo|").append(userInfo).append("|ContactInfo|").append(contactInfo);
return sb.toString();
}
static class FaceBookUserBuilder{
FaceBookUser user;
public FaceBookUserBuilder(String userName){
this.user = new FaceBookUser(userName);
}
public FaceBookUserBuilder setUserBasicInfo(UserBasicInfo info){
user.setUserBasicInfo(info);
return this;
}
public FaceBookUserBuilder setContactInfo(ContactInfo info){
user.setContactInfo(info);
return this;
}
public FaceBookUser build(){
return user;
}
}
}
public class BuilderPattern{
public static void main(String args[]){
FaceBookUser fbUser1 = new FaceBookUser.FaceBookUserBuilder("Ravindra").build(); // Mandatory parameters
UserBasicInfo info = new UserBasicInfo("sunrise","25-May-1975","M");
// Build User name + Optional Basic Info
FaceBookUser fbUser2 = new FaceBookUser.FaceBookUserBuilder("Ravindra").
setUserBasicInfo(info).build();
// Build User name + Optional Basic Info + Optional Contact Info
ContactInfo cInfo = new ContactInfo("xxx@xyz.com","1111111111","2222222222");
FaceBookUser fbUser3 = new FaceBookUser.FaceBookUserBuilder("Ravindra").
setUserBasicInfo(info).
setContactInfo(cInfo).build();
System.out.println("Facebook user 1:"+fbUser1);
System.out.println("Facebook user 2:"+fbUser2);
System.out.println("Facebook user 3:"+fbUser3);
}
}
输出:
Facebook user 1:|User|Ravindra|UserInfo|null|ContactInfo|null
Facebook user 2:|User|Ravindra|UserInfo|Name:DOB:Gender:sunrise:25-May-1975:M|ContactInfo|null
Facebook user 3:|User|Ravindra|UserInfo|Name:DOB:Gender:sunrise:25-May-1975:M|ContactInfo|email:mobile(H):mobile(W):xxx@xyz.com:1111111111:2222222222
说明:
FaceBookUser
是一个复杂的对象,具有使用合成的以下属性:
String userName;
UserBasicInfo userInfo;
ContactInfo contactInfo;
FaceBookUserBuilder
是一个静态构建器类,包含并构建FaceBookUser
。
userName只是构建FaceBookUser的必需参数
FaceBookUserBuilder
通过设置可选参数来构建FaceBookUser
:UserBasicInfo
和ContactInfo
此示例说明了三个不同的FaceBookUsers
,它们具有不同的属性,由Builder构建。
在此示例中,使用了合成而不是在Builder类中复制FaceBookUser的所有属性。
修改强>
将所有相关属性分组为逻辑类。在FaceBookUser中定义所有这些类。不是在Builder
中再次添加所有这些成员变量,而是在FaceBookUser
类中包含Builder
。
为简单起见,我添加了两个类:UserBasicInfo和ContactInfo。现在使用其他属性(如
)来爆炸这个FaceBookUser类NewsFeed
Messages
Friends
Albums
Events
Games
Pages
Ads
等
如果您同时复制Builder
和FaceBookUser
中的所有这些属性,则代码将难以管理。相反,通过在FaceBookUser
本身使用FaceBookUserBuilder
的组合,您可以简单地构建过程。
添加上述属性后,您将像往常一样逐步构建FaceBookUser
。
会是这样的:
FaceBookUser fbUser3 = new FaceBookUser.FaceBookUserBuilder("Ravindra").
setUserBasicInfo(info).
setNewsFeed(newsFeed).
setMessages(messages).
setFriends(friends).
setAlbums(albums).
setEvents(events).
setGames(games).
setAds(ads).build();