我读到当你有一个包含大量参数的类时,使用构建器模式很有用.我想知道如何使用构建器模式实现实体.如果您能提供示例代码,那就太棒了.
我想将Web服务URL请求表示为对象,并发现有许多常见参数可以在继承层次结构中"冒泡".一个请求可能有很多参数,一些是必需的,另一些是可选的,我相信Bloch的Builder模式是一个很好的选择,用一个流畅的接口模拟命名参数.
具体来说,我正在设计Google Maps Web服务API,它具有一般的Web服务请求
http://maps.googleapis.com/maps/api/service/output?{parameters}
Run Code Online (Sandbox Code Playgroud)
service并且output是必需参数和sensor必需参数.还有一个可选参数language.
每个服务都有一组强制和可选参数.地理编码服务有两个可选参数,bounds和region.它还具有互斥的强制参数,address或者location指定服务类型(分别为直接或反向地理编码).我用新的儿童班来代表这种互斥.
我想象类层次结构如下:
.-----.
| Url |
'-----'
^
|
.---------.
| Request |
'---------'
^
|----------------------------+--------------...
.---------. .------------.
| Geocode | | Directions |
'---------' '------------'
^ ^
|------------+ .
.--------. .---------. .
| Direct | | Reverse | .
'--------' '---------'
Run Code Online (Sandbox Code Playgroud)
然后,我想做类似以下的事情:
String output = "xml";
boolean sensor = true;
String address = "Av. Paulista, São Paulo, Brasil"; …Run Code Online (Sandbox Code Playgroud) 以下哪项是实施构建器模式的更好方法?
1)使用对象构建而不是构建器中的所有属性(并在构建器构造器中创建它):
public class Person {
private String firstName;
// other properties ...
private Person() {}
// getters ...
public static class Builder {
// person object instead of all the person properties
private Person person;
public Builder() {
person = new Person();
}
public Builder setFirstName(String firstName) {
person.firstName = firstName;
return this;
}
// other setters ...
public Person build() {
if (null == person.firstName) {
throw new IllegalStateException("Invalid data.");
}
return person;
}
}
}
Run Code Online (Sandbox Code Playgroud)
2)直接在构建器中使用对象的属性来构建而不是对象(并在build()方法中创建它):
public …Run Code Online (Sandbox Code Playgroud) 考虑以下类似于构建器的类,它最终允许我构造一个具有成员变量的某些(运行时)值的对象,以及嵌入由多个(编译时)类型携带的某些行为.
相同的构建允许更新成员变量(通常的构建器模式),以及更改与构建器的类型携带状态相关联的模板类型参数(仅显示有几个模板类型参数和成员,但在实践中,会有更多):
template <typename T1 = DefaultT1, typename T2 = DefaultT2>
class Builder {
int param1, param2;
Builder(int param1, int param2) : param1{param1}, param2{param2} {}
public:
Builder() : Builder(default1, default2) {}
// methods to change param1 and param2 not shown
/* return a new Builder with T1 changed to the given T1_NEW */
template <typename T1_NEW>
Builder<T1_NEW, T2 > withT1() { return {param1, param2}; }
template <typename T2_NEW>
Builder<T1 , T2_NEW> withT2() { return {param1, param2}; …Run Code Online (Sandbox Code Playgroud) Joshua Bloch的Effective Java描述了一个Builder模式,可用于构建具有多个可选定制参数的对象.他为Builder函数建议的命名约定,"模拟在Ada和Python中找到的命名可选参数",似乎不符合Java的标准命名约定.Java函数往往依赖于使用动词来启动函数,然后依靠基于名词的短语来描述它的作用.Builder类只有该函数定义的变量的名称.
Java标准库中是否有使用Builder模式的API?我想在继续使用之前,将本书中的建议与核心Java库集中的实际实现进行比较.
我想将一些数据写入XML文件(XML文件将达到~50 MB).
我找到了nokogiri(1.5.0)宝石 最多高效解析(只读和不写).Nokogiri不是写入XML文件的好选择,因为它在内存中保存完整的XML数据,直到写入最终将其写下来.
我发现builder(3.0.0)是一个不错的选择,但不确定它是否是最好的选择.
我用以下简单的代码尝试了一些基准测试:
(1..500000).each do |k|
xml.products {
xml.widget {
xml.id_ k
xml.name "Awesome widget"
}
}
end
Run Code Online (Sandbox Code Playgroud)
Nokogiri大约需要143秒,内存消耗也逐渐增加,最终达到700 MB左右.
Builder大约需要123秒,内存消耗足够稳定在10 MB.
那么在Ruby中编写大量XML文件(50 MB)有更好的解决方案吗?
Nokogiri文件:
require 'rubygems'
require 'nokogiri'
a = Time.now
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
(1..500000).each do |k|
xml.products {
xml.widget {
xml.id_ k
xml.name "Awesome widget"
}
}
end
}
end
o = File.new("test_noko.xml", "w")
o.write(builder.to_xml)
o.close
puts (Time.now-a).to_s
Run Code Online (Sandbox Code Playgroud)
生成器文件:
require 'rubygems'
require 'builder'
a = Time.now
File.open("test.xml", 'w') {|f|
xml …Run Code Online (Sandbox Code Playgroud) 我目前使用构建器模式来构建我的MVC视图模型.
var viewModel = builder
.WithCarousel(),
.WithFeaturedItems(3),
.Build()
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是当我必须对异步方法进行服务调用时.这意味着我的构建器方法必须返回Task<HomeViewModelBuilder>而不是HomeViewModelBuilder.这可以防止我像我一样链接构建方法await.
示例方法
public async Task<HomeViewModelBuilder> WithCarousel()
{
var carouselItems = await _service.GetAsync();
_viewModel.Carousel = carouselItems;
return this;
}
Run Code Online (Sandbox Code Playgroud)
现在我必须使用await调用构建器方法.
await builder.WithCarousel();
await builder.WithFeaturedItems(3);
Run Code Online (Sandbox Code Playgroud)
有没有人使用构建器模式的异步方法?如果是这样,是否可以链接方法或推迟await构建方法.
例如,如果我有一个构建器,那么我可以像这样创建对象:
Node node = NodeBuilder()
.withName(someName)
.withDescription(someDesc)
.withData(someData)
.build();
Run Code Online (Sandbox Code Playgroud)
如何确保在构建方法之前已经设置了用于构建对象的所有变量?
例如:
Node node = NodeBuilder()
.withName(someName)
.build();
Run Code Online (Sandbox Code Playgroud)
不是一个有用的节点,因为尚未设置描述和数据.
我使用构建器模式的原因是因为没有它,我需要很多构造函数的组合.例如,可以通过获取Field对象来设置名称和描述,并且可以使用文件名设置数据:
Node node = NodeBuilder()
.withField(someField) //Sets name and description
.withData(someData) //or withFile(filename)
.build(); //can be built as all variables are set
Run Code Online (Sandbox Code Playgroud)
否则将需要4个构造函数(字段,数据),(字段,文件名),(名称,描述,数据),(名称,描述,文件名).当需要更多参数时会变得更糟.
这些"方便"方法的原因是因为必须构建多个节点,因此它可以节省大量重复的行,如:
Node(modelField.name, modelField.description, Data(modelFile)),
Node(dateField.name, dateField.description, Data(dateFile)),
//etc
Run Code Online (Sandbox Code Playgroud)
但是在某些情况下,需要使用非来自文件的数据构建节点,和/或名称和描述不基于字段.也可能有多个节点共享相同的值,因此不是:
Node(modelField, modelFilename, AlignLeft),
Node(dateField, someData, AlignLeft),
//Node(..., AlignLeft) etc
Run Code Online (Sandbox Code Playgroud)
你可以有:
LeftNode = NodeBuilder().with(AlignLeft);
LeftNode.withField(modelField).withFile(modelFilename).build(),
LeftNode.withField(dateField).withData(someData).build()
Run Code Online (Sandbox Code Playgroud)
所以我认为我的需求与构建器模式非常匹配,除了构建不完整对象的能力.由于上述原因,"在构造函数中放置必需参数并具有可选参数的构建器方法"的正常建议不适用于此处.
实际问题:如何在编译时调用构建之前确保已设置所有参数?我正在使用C++ 11.
(在运行时,我可以为每个参数设置一个标志位,并声明所有标志都在构建中设置)
或者是否有一些其他模式来处理大量的构造函数组合?
我有一个变量类,我不希望它为null或空.有没有办法使用Lombok构建器来设置属性?我可以使用,@NonNull但我无法验证它是否为空.显然,另一种选择是编写我自己的构建器来执行所有这些检查.例如:
class Person {
@NonNull
private String firstName;
@NonNull
private String lastName;
public static class PersonBuilder() {
// .
// .
// .
public Person build() {
//do checks for empty etc and return object
}
}
}
Run Code Online (Sandbox Code Playgroud)