在这篇短文中,我们将介绍构造器模式的表亲--精简构造器。
不像传统的构造器,需要一个单独的构造器对象,精简构造器重用对象本身来提供构造器的功能。
我们来看一个例子:
精简构造器
pub struct Shape {
position: Vec3,
geometry: Geometry,
material: Option<Material>,
}
impl Shape {
pub fn new(geometry: Geometry) -> Shape {
Shape {
position: Vec3::default(),
geometry,
material: None,
}
}
pub fn with_position(mut self, position: Vec3) -> Shape {
self.position = position;
self
}
pub fn with_material(mut self, material: Material) -> Shape {
self.material = Some(material);
self
}
}
// Call site
let shape = Shape::new(Geometry::Sphere::with_radius(1))
.with_position(Vec3(0, 9, 2))
.with_material(Material::SolidColor(Color::Red));
完整构造器
相比之下,完整构造器在定义上明显更加冗长,且需要两次额外的调用,如下:
pub struct Shape {
position: Vec3,
geometry: Geometry,
material: Option<Material>,
}
pub struct ShapeBuilder {
position: Option<Vec3>,
geometry: Option<Geometry>,
texture: Option<Texture>,
}
impl Shape {
pub fn builder() -> ShapeBuilder { ... }
}
impl ShapeBuilder {
pub fn position(&mut self, position: Vec3) -> &mut Self { ... }
pub fn geometry(&mut self, geometry: Geometry) -> &mut Self { ... }
pub fn material(&mut self, material: Material) -> &mut Self { ... }
pub fn build(&self) -> Shape { ... }
}
// Call site
let shape = Shape::builder()
.position(Vec3(9, 2))
.geometry(Geometry::Sphere::with_radius(1))
.material(Material::SolidColor(Color::Red))
.build();
精简构造器最主要的好处就是它可以增量的、零成本的方式构建对象。因此,在代码以不确定的方向快速发展的情况下,它特别有用。在工作中,找到了一个实际的例子,如下:
impl PeerManagerActor {
pub fn new(
store: Store,
config: NetworkConfig,
client_addr: Recipient<NetworkClientMessages>,
view_client_addr: Recipient<NetworkViewClientMessages>,
routing_table_addr: Addr<RoutingTableActor>,
) -> anyhow::Result<Self> {
这里有一个 new 方法,及一大堆各种依赖的参数。我们需要做的是,还要增加另外的依赖,为了能在测试中被重写。首先尝试的是,在 new 方法中加入新的参数,如下:
pub fn new(
store: Store,
config: NetworkConfig,
client_addr: Recipient<NetworkClientMessages>,
view_client_addr: Recipient<NetworkViewClientMessages>,
routing_table_addr: Addr<RoutingTableActor>,
+ ping_counter: Box<dyn PingCounter>,
) -> anyhow::Result<Self> {
然而这种方式,我们需要修改7处调用 new 方法的地方。替换成精简构造器模式,我们就只需要修改一处,也就是我们关心的counter部分。
命名注意事项
如果构造器的方法恰巧被使用了,with_foo 是最好的命名方式。对于布尔类型,有时需要同时写2个构造器方法,如下:
pub fn fancy(mut self) -> Self {
self.with_fancy(true)
}
pub fn with_fancy(mut self, yes: bool) -> Self {
self.fancy = yes;
self
}
本文翻译自:
https://matklad.github.io/2022/05/29/builder-lite.html