Keeping builder in separate class (fluent interface)
1 2 3 4 5 | Foo foo = Foo.builder() .setColor(red) .setName("Fred") .setSize(42) .build(); |
所以我知道在调用方法时有以下"Builder"解决方案用于创建命名参数。 虽然,这似乎只适用于内部静态类作为构建器,或者我错了吗? 我看了一些构建器模式的教程,但是对于我想要做的事情,它们看起来非常复杂。 是否有任何方法可以保持Foo类和Builder类分离,同时享受上述代码等命名参数的好处?
以下是典型设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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; } |
您可以确定将Builder类的字段更改为私有 - 然后您只需要为构建器上的每个"属性"使用(公共)getter方法;并且Foo中的构造函数调用这些方法;而不是仅仅获取Builder对象中的字段。
然后你可以将你的Builder类移出Foo。简单明了。
但请记住:最后,Builder和Foo密切相关。它们按设计共享一组共同的字段。所以对Foo的任何改变都会影响Builder;反之亦然。因此,让它们"紧密结合"是很有意义的。也许不是内部/外部类,但可能仍然在同一个源文件中!但是......其中只有一个可以公开。这真的是你想要的吗?!
换句话说:不要只是"因为你可以"撕裂事物。只有在你有充分理由这样做的情况下才能做到这一点,并且如果出现的事情比你当前的解决方案更好!
编辑:您的问题可能不是Foo和Builder的分离,而是您的Foo类首先包含太多字段。不要忘记单一的责任原则......当你的班级需要超过5,6个领域......它可能做得太多而且应该进一步切片!请记住:良好的OO设计首先是关于行为;不是在某个对象中有10,20个字段!
很难严格定义"构建器模式?",并且设计选择有几个自由度。有些概念很容易被混合或滥用,除此之外,说"你总是必须这样做"通常很难(而且几乎总是错误的)。
问题是应用"模式"应该实现什么。在您的问题和示例中,您已经混合了两个概念,即构建器模式和流畅的接口。扮演魔鬼的拥护者,人们甚至可以偷偷地争辩说,你的案例中的"建造者"只是托马斯已经提到过的参数对象,它是以一种特殊的方式(流利地)构建的,并且富含了
构建器模式的一些可能目标是重叠或齐头并进。但你应该问问自己,你的主要目标是什么:
-
结果对象应该是不可变的吗?
-
它应该是真正不可变的,只有
final 最后的字段,还是可能还有不应该公开的setter? (建造者仍然可以称这些非公开的制定者!)
-
它应该是真正不可变的,只有
- 目标是限制一般的可见性吗?
- 应该有多态实例化吗?
- 总结大量构造函数参数的主要目标是什么?
-
主要目标是使用流畅的界面提供简单的配置,并管理"默认"值吗?
...
因为所有这些问题都会对设计中的微妙差异产生影响。但是,关于你的实际的,高水平的,"句法"问题:
-
您可以将构建器设计为
public static 内部类(您在示例中所做的操作)。1
2
3
4
5
6
7
8
9public class Person {
...
public static PersonBuilder create() { ... }
public static class PersonBuilder {
...
public Person build() { ... }
}
}这提供了最严格的隐私形式:
Person 和PersonBuilder 的构造函数都可以是private 。 -
您还可以将实际的类及其构建器放在单独的文件中:
1
2
3public class Person {
...
}和
1
2
3public class PersonBuilder {
...
}这里可以实现合理程度的隐私:两者的构造函数可以是包私有的(即具有默认可见性)。
在这两种情况下,除了构建器类的名称(
作为构建器模式的替代方法,您还可以使用参数对象:
如果您愿意,可以在此处使用getter和setter,而不是公共字段。
然后
1 2 3 4 5 | public Foo(FooParams params) { this.size = params.size; this.color = params.color; this.name = params.name; } |
使用成分。为了使事情更简单,更清晰,请不要复制源(
例如,在
简单的代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 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("[email protected]","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); } } |
输出:
1 2 3 | 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 |
说明:
1 2 3 |
userName只是构建FaceBookUser的必需参数
此示例说明了使用Builder构建的具有不同属性的三个不同
在此示例中,使用了合成而不是在Builder类中复制FaceBookUser的所有属性。
编辑:
将所有相关属性分组为逻辑类。在FaceBookUser中定义所有这些类。不是在
为简单起见,我添加了两个类:UserBasicInfo和ContactInfo。现在使用其他属性来爆炸这个FaceBookUser类
1 2 3 4 5 6 7 8 | NewsFeed Messages Friends Albums Events Games Pages Ads |
等等
如果在
添加上述属性后,您将像往常一样逐步构建
它会是这样的:
1 2 3 4 5 6 7 8 9 | FaceBookUser fbUser3 = new FaceBookUser.FaceBookUserBuilder("Ravindra"). setUserBasicInfo(info). setNewsFeed(newsFeed). setMessages(messages). setFriends(friends). setAlbums(albums). setEvents(events). setGames(games). setAds(ads).build(); |