skinny85 / jilt

Java annotation processor library for auto-generating Builder (including Staged Builder) pattern classes
Other
240 stars 13 forks source link

Add Support for Returning Wrapper Instances from Builder #21

Closed making closed 4 months ago

making commented 4 months ago

We have a use case where I need to validate arguments using a validator before creating an instance of a class with a builder. Using YAVI, I can achieve this by validating the arguments and representing the validated state with a Validated class (similar to Optional).

Here's an example with YAVI:

public record Car(String manufacturer, String licensePlate, Integer seatCount) {

    static Arguments3Validator<String, String, Integer, Car> validator = Yavi.arguments()
            ._string("manufacturer", c -> c.notNull())
            ._string("licensePlate", c -> c.notNull().greaterThanOrEqual(2).lessThanOrEqual(14))
            ._integer("seatCount", c -> c.greaterThanOrEqual(2))
            .apply(Car::new);
}
Validated<Car> carValidated = validator.validate("Morris", "DD-AB-123", 2);

Currently, with the builder pattern in Jilt, the only option is to return an instance of the target class (e.g., Car) directly. However, I would like the option to return an instance of a wrapper class like Validated<Car> instead of the target class.

The usage would look like this (?):

@Builder(builderTarget = Car.class)
public static Validated<Car> validate(String manufacturer, String licensePlate, Integer seatCount) {
    return Car.validator.validate(manufacturer, licensePlate, seatCount);
}
Validated<Car> validatedCar = CarBuilder.car()
        .manufacturer("Morris")
        .licensePlate("DD-AB-123")
        .seatCount(2)
        .build();
skinny85 commented 4 months ago

Hi @making,

thanks for opening the issue!

Currently, with the builder pattern in Jilt, the only option is to return an instance of the target class (e.g., Car) directly.

Not sure what part of the documentation gave you that impression, but that's not correct - you can return any class from your Builder that you want!

So, using your example, if you do this:

@Builder(className = "CarBuilder", factoryMethod = "car")
public static Validated<Car> validate(String manufacturer, String licensePlate, Integer seatCount) {
    return Car.validator.validate(manufacturer, licensePlate, seatCount);
}

It will generate a Builder that can be used exactly like you wanted:

Validated<Car> validatedCar = CarBuilder.car()
        .manufacturer("Morris")
        .licensePlate("DD-AB-123")
        .seatCount(2)
        .build();

Let me know if this makes sense!

Thanks, Adam

making commented 4 months ago

Thanks! It seems I misunderstood something by looking at the source code that was generated when I tried something. I was able to get a great integration!

skinny85 commented 4 months ago

Of course, glad you got it working!