23 Haziran 2020 Salı

Lombok @Builder Anotasyonu - Nesneyi Builder Şeklinde Kurabilmemizi Sağlar

Giriş
Şu satırı dahil ederiz
import lombok.Builder;
Sınıfa @Builder anotasyonu eklenir daha sonra Foo.builder().field1().field2().build() şeklinde kullanılır. Setter olarak setField1() değil field1() şeklinde metodlar sağlıyor.

Örnek
Elimizde şöyle bir kod olsun.
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@Entity
public class Customer {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String firstName;
  private String lastName;
  private String birthdate;
}
Şöyle kullanabiliriz.
public Customer mapFieldSet(FieldSet fieldSet) throws BindException {
    return Customer.builder()
      .id(fieldSet.readLong("id"))
      .firstName(fieldSet.readRawString("firstName"))
      .lastName(fieldSet.readRawString("lastName"))
      .birthdate(fieldSet.readRawString("birthdate"))
      .build();
  }
Varsayılan Değerler Nasıl Yapılır
Örnek
Şöyle yaparız. Burada varsayılan değerler veriliyor. Bu varsayılan değerler sadece Builder.build() kullanımında işe yarıyor.
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class WebClientComposer {

  static final boolean DEFAULT_SOCKET_KEEP_ALIVE_ENABLED = true;
  static final boolean DEFAULT_HTTP_PROXY_ENABLED = false;
  static final int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 5000;
  static final int MAX_IN_MEMORY_SIZE_BYTES = 1024 * 1024;

  @Builder.Default
  private final String baseUrl = "";
  @Builder.Default
  private final int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT_MILLIS;
  @Builder.Default
  private final int readTimeoutSecs = 0;
  @Builder.Default
  private final boolean socketKeepAlive = DEFAULT_SOCKET_KEEP_ALIVE_ENABLED;
  @Builder.Default
  private final int maxInMemorySize = MAX_IN_MEMORY_SIZE_BYTES;
  @Builder.Default
  private final int maxPoolConnectionSize = ...;
  ...
}
Mecburi Alan (Mandatory Field) Nasıl Yapılır
Örnek
Şöyle yaparız. Burada mecburi alan lombok.NonNull ile işaretli. Ayrıca Lombok builde döndürent metodun ismini de belirtiyoruz ve kullanıyoruz
@Data @Builder(builderMethodName = "internalBuilder") static class Student { @NonNull private String firstName; private String lastName; private String studentId; private Integer year; private List<Integer> marks; public static StudentBuilder builder(String name) { return internalBuilder().firstName(name); } }
Calculate The Value of a Field Based On Another Mandatory Field
Örnek
Şöyle yaparız
@Data public class Student { @NonNull private String firstName; private String lastName; private String studentId; private Integer year; private List<Integer> marks; public static StudentBuilder builder(String name) { return internalBuilder().firstName(name); } @Builder(builderMethodName = "internalBuilder") private Student(String firstName, String lastName, List<Integer> marks, Integer year) { this.firstName = firstName; this.lastName = lastName; this.marks = marks; this.year = year; //generate some value based on other fields this.studentId = generateStudentId(firstName); } private String generateStudentId(String firstName) { return firstName + new Random().nextInt(1000); } }
Ancak bu kullanımı anlamak zor. Bunun yerine Fluent Setters kullanılabilir. Şöyle yaparız
@Data @Accessors(chain = true, fluent = true) public class Student { private String firstName; private String lastName; private String studentId; private Integer year; private List<Integer> marks; public Student(@NonNull String firstName) { this.firstName = firstName; this.studentId = generateStudentId(firstName); } private String generateStudentId(String firstName) { return firstName + new Random().nextInt(1000); } } Student student = new Student("John").lastName("Doe").year(2);

Anotasyonun Alanları

builderClassName Alanı
Örnek - Varsayılan Değeler
Şöyle yaparız
@Builder(builderMethodName = "methodBuilder", buildMethodName = "call", builderClassName = "MethodBuilder") method(String a, String b, String c) { ... acutal logic here ... } private class MethodBuilder { private String a = "Hello"; private String b = "builder"; private String c = "world!"; }
buildMethodName  Alanı
Lombok tarafından üretilecek metod ismini belirtir. Böylece gerekirse bu metod çağırmak istersek ismini bilebiliriz.
Örnek
Şöyle yaparız
@Builder(builderMethodName = "methodBuilder", buildMethodName = "call") void method(@NotNull String firstParam, @NotNull String secondParam, String thirdParam, String fourthParam, Long fifthParam, @NotNull Object sixthParam) { ... } methodBuilder() .firstParam("A") .secondParam("B") .sixthParam(new Object()) .call(); methodBuilder() .firstParam("A") .secondParam("B") .thirdParam("C") .fifthParam(2L) .sixthParam("D") .call(); methodBuilder() .firstParam("A") .secondParam("B") .fifthParam(3L) .sixthParam(this)         .call();
Örnek - Generic Code
Şöyle yaparız
@Builder(builderMethodName = "methodBuilder", buildMethodName = "call", builderClassName = "MethodBuilder") public <T extends Collection> T read(final byte[] content, final Class<T> type) {...} public <T extends Collection> MethodBuilder<T> methodBuilder(final Class<T> type) { return new MethodBuilder<T>().type(type); } public class MethodBuilder<T extends Collection> { private Class<T> type; public MethodBuilder<T> type(Class<T> type) { this.type = type; return this; } public T call() { return read(content, type); } }
veya şöyle yaparız
@Builder(builderMethodName = "methodBuilder", buildMethodName = "call", builderClassName = "MethodBuilder") public <T extends Collection> T read(final byte[] content, final Class<T> type) {...} public class MethodBuilder<T extends Collection> { private Class<T> type; public <L extends Collection> MethodBuilder<L> type(final Class<L> type) { this.type = (Class)type; return (MethodBuilder<L>) this; } public T call() { return read(content, type); } }

toBuilder Alanı
Nesneyi kurmak için builder() metodu yanında, nesnenin kopyasını almak için toBuilder() metodu sağlar.
Örnek - true Verirsek Clone Yapılabilir
Açıklaması şöyle
Finally, the toBuilder = true setting adds an instance method toBuilder() that creates a builder object populated with all the values of that instance. This enables an easy way to create a new instance prepopulated with all the values from the original instance and change just the fields needed. This is particularly useful for @Value classes because the fields are immutable.
Şöyle yaparız
@Builder(toBuilder=true)
class Foo {
   int x;
   ...
}

Foo f0 = Foo.builder().build();
Foo f1 = f0.toBuilder().x(42).build();

Hiç yorum yok:

Yorum Gönder