Java 使用Builder同时构建封装对象
假设我有一个封装了另一个类的类:Java 使用Builder同时构建封装对象,java,design-patterns,encapsulation,builder,lombok,Java,Design Patterns,Encapsulation,Builder,Lombok,假设我有一个封装了另一个类的类: @Builder 公务舱龙{ 私人层面; 私有字符串名称; 公共静态类ParentBuilder{ DimensionsBuilder innerBuilder=Dimensions.builder(); 公共建筑高度(双高){ this.innerBuilder.height(高度); 归还这个; } 公共长度(双倍长度){ this.innerBuilder.length(长度); 归还这个; } 公龙大厦({ returndragon.builder()
@Builder
公务舱龙{
私人层面;
私有字符串名称;
公共静态类ParentBuilder{
DimensionsBuilder innerBuilder=Dimensions.builder();
公共建筑高度(双高){
this.innerBuilder.height(高度);
归还这个;
}
公共长度(双倍长度){
this.innerBuilder.length(长度);
归还这个;
}
公龙大厦({
returndragon.builder()
.dimensions(此.innerBuilder.build())
.name(此.name)
.build();
}
}
}
@建筑商
公共类维度{
私人双高;
私人双倍长度;
}
请记住,这是一个非常简化的示例,真正的代码(不幸的是,与龙无关)将大量属性委托给innerBuilder
这样,我可以像这样实例化类:
Dragon dragon = Dragon.builder()
.dimensions()
.height(12.0)
.length(25.0)
.back()
.name("Smaug")
.build();
Dragon=Dragon.builder()
.高度(12.0)
.长度(25.0)
.名称(“Smaug”)
.build();
而不是像这样:
Dragon dragon = Dragon.builder()
.dimensions()
.height(12.0)
.length(25.0)
.back()
.name("Smaug")
.build();
Dragon=Dragon.builder()
.dimensions(dimensions.builder()
.高度(12.0)
.长度(25.0)
.build())
.名称(“Smaug”)
.建造;
添加生成器方法来直接构建内部类是否也是一种良好的编码实践?或者它违反了一些设计原则,因为它可能是紧密耦合的
我已经遇到的一个问题是,在对内部类进行重构时,我还必须对父类应用几乎相同的重构。在我看来,从样式/设计的角度来看,您的方法没有根本上的错误。但是,正如用户JB Nizet在评论中解释的,存在两个主要问题:
@Delegate
在这里帮不了你,因为它不能处理Lombok本身生成的类。)dimensions(dimensions)
和委托方法,这非常令人困惑Dragon dragon = Dragon.builder()
.dimensions()
.height(12.0)
.length(25.0)
.back()
.name("Smaug")
.build();
这就是实现它的方法(使用Lombok 1.18.8):
维度的生成器保存对容器的引用DragonBuilder
:
// Don't let Lombok create a builder() method, so users cannot
// instantiate builders on their own.
@Builder(builderMethodName = "")
public class Dimensions {
private double height;
private double length;
public static class DimensionsBuilder {
private Dragon.DragonBuilder parentBuilder;
// The only constructor takes a reference to the containing builder.
DimensionsBuilder(Dragon.DragonBuilder parentBuilder) {
this.parentBuilder = parentBuilder;
}
// Provide a method that returns the containing builder.
public Dragon.DragonBuilder back() {
return parentBuilder;
}
// The build() method should not be called directly, so
// we make it package-private.
Dimensions build() {
return new Dimensions(height, length);
}
}
}
这种方法可以扩展,因为Lombok会在构建器中自动生成所有必要的剩余setter方法。
此外,由于用户提供了自己的维度
实例,因此可能不会有什么意外。(您可以允许这样做,但我强烈建议您在运行时检查潜在的冲突,例如,检查是否已调用这两个方法。)
缺点是,Dimensions.builder()
不再可用,因此不能直接在具有Dimensions
字段的其他类的生成器中使用它。但是,也有一个解决方案:使用@SuperBuilder Dimensions
并定义一个类嵌套DimensionsBuilder扩展Dimensions。DimensionsBuilder
在DragonBuilder中,主要问题是您在问题末尾描述的问题:它不可缩放。如果您的外部类委托给5个构建器,那么最终将有几十个方法,并且每次更改5个构建器中的任何一个时都必须进行更改。您将忘记将内部构建器中的更改复制到外部构建器。你会有矛盾。比如说,如果你的外部物体需要两个维度,而不是一个维度呢?你违反了单一责任原则。我还发现,这使得外部构建器/类的结构不那么清晰。这也使得调用方更难只传递现有维度对象,或者重用创建维度对象的现有代码。最后一点确实是另一个问题。您仍然可以调用Dragon.builder().dimensions(someDimensions).build()
,但提供的someDimensions
对象将永远不会被使用。