PersonLombokSecure.java

package fr.univtln.bruno.samples.java101.tp1.lombok;

import lombok.*;

import java.util.Objects;

/**
 * Secure Lombok-backed Person: validation is performed at the factory/builder boundary.
 *
 * <p>This class demonstrates a recommended pattern when using Lombok:
 * the public, validated static factory method {@link #of(String, String, int)} is
 * annotated with {@code @Builder}. Lombok generates a builder whose {@code build()}
 * method delegates to this factory method; therefore both calls to
 * {@code PersonLombokSecure.of(...)} and to the generated builder
 * {@code PersonLombokSecure.builder()...build()} use the same validation
 * and normalization logic implemented in {@code of(...)}.</p>
 *
 * <p>Implementation notes:
 * <ul>
 *   <li>The all-args constructor is private (generated by Lombok with
 *       {@code @AllArgsConstructor(access = AccessLevel.PRIVATE)}), ensuring
 *       instances can only be created through the validated factory or the
 *       generated builder.</li>
 *   <li>The factory performs lightweight validation (null checks, range checks)
 *       and normalization (e.g. {@code String.strip()}) before creating the
 *       instance. Validation uses {@link Objects#requireNonNull} and throws
 *       {@link IllegalArgumentException} for invalid numeric values.</li>
 *   <li>We intentionally avoid using {@code @Data} here to keep the example
 *       explicit; use {@code @Data} only when you want the convenience of
 *       generating getters/setters and other boilerplate in one go.</li>
 * </ul>
 */
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@ToString
public class PersonLombokSecure {
    private final String firstName;
    private final String lastName;
    @Setter
    private int age;

    /**
     * Private no-arg constructor kept for tools and to avoid a missing Javadoc
     * warning for the implicit default constructor. Instances should be created
     * through the validating factory {@link #of(String,String,int)} or the
     * Lombok-generated builder.
     */
    @SuppressWarnings("unused")
    private PersonLombokSecure() {
        this.firstName = "";
        this.lastName = "";
        this.age = 0;
    }

    @Builder
    public static PersonLombokSecure of(String firstName, String lastName, int age) {
        Objects.requireNonNull(firstName, "firstName must not be null");
        Objects.requireNonNull(lastName, "lastName must not be null");
        if (age < 0) throw new IllegalArgumentException("age must be >= 0");
        return new PersonLombokSecure(firstName.strip(), lastName.strip(), age);
    }
}