java encapsulated_Using Builder to also build encapsulated object

In my opinion, there is nothing fundamentally wrong with your approach from a style/design perspective. However, as explained by user JB Nizet in the comments, there are two major problems:

You run into maintenance problems, because you have to duplicate every foreign builder method. (Lombok's @Delegate can't help you here, because it does not work on classes generated by Lombok itself.)

Users of your builder can invoke both dimensions(Dimensions) and the delegation methods, which is very confusing.

From a user perspective, I would like the builder to be used like this:

Dragon dragon = Dragon.builder()

.dimensions()

.height(12.0)

.length(25.0)

.back()

.name("Smaug")

.build();

This is how you can achieve it (using Lombok 1.18.8):

@Builder

public class Dragon {

private Dimensions dimensions;

private String name;

public static class DragonBuilder {

private Dimensions.DimensionsBuilder innerBuilder =

new Dimensions.DimensionsBuilder(this);

// If a method of the same name exists, Lombok does not generate

// another one even if the parameters differ.

// In this way, users cannot set their own dimensions object.

public Dimensions.DimensionsBuilder dimensions() {

return innerBuilder;

}

// Customize build() so that your innerBuilder is used to create

// the Dimensions instance.

public Dragon build() {

return new Dragon(innerBuilder.build(), name);

}

}

}

The builder for Dimensions holds a reference to the container 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);

}

}

}

This approach scales because Lombok automatically generates all necessary remaining setter methods in the builders. Furthermore, there may be no surprises due to users providing their own Dimensions instance. (You could allow that, but I strongly suggest to do runtime checks for potential conflicts then, e.g. by checking whether both methods have been called.)

The drawback is that Dimensions.builder() is not available any more, so it cannot be used directly or in builders of other classes that have a Dimensions field. However, there is also a solution for that: Use @SuperBuilder Dimensions and define a class NestedDimensionsBuilder extends Dimensions.DimensionsBuilder within DragonBuilder.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值