builder 어노테이션을 쓰면 클래스를 대상으로 복잡한 builder API를 만들 수 있다.
Person.builder()
.name("Adam Savage")
.city("San Francisco")
.job("Mythbusters")
.job("Unchained Reaction")
.build();
이렇게 체이닝하는 게 가능하단 것.
@Builder는 클래스, 생성자 또는 메서드에 적용할 수 있습니다.
"클래스에 적용" 및 "생성자에 적용" 모드가 가장 일반적인 사용 사례이지만,
@Builder는 "메서드" 사용 사례로 가장 쉽게 설명됩니다.
메서드에 @Builder를 적용하면...
1. 같은 타입의 매개변수를 가진 정적 메서드(빌더라고 함)와 동일한 타입 인자를 가진 inner 정적 클래스인 FooBuilder가 생성됩니다.
2. 빌더 내부: 각 대상 매개변수마다 하나의 private 비정적 비최종 필드가 생성됩니다.
3. 빌더 내부: 패키지 프라이빗한 매개변수 없는 빈 생성자가 생성됩니다.
4. 빌더 내부: 대상 매개변수마다 'setter'와 유사한 메서드가 생성됩니다. 이 메서드는 해당 매개변수와 동일한 타입 및 이름을 가지며 빌더 자체를 반환합니다. 따라서 위 예제와 같이 setter 호출을 연결하여 사용할 수 있습니다.
5. 빌더 내부: 대상 메서드를 호출하고 각 필드를 전달하는 build() 메서드가 생성됩니다. 이 메서드는 대상이 반환하는 것과 동일한 타입을 반환합니다.
6. 빌더 내부: 합리적인 toString() 구현이 생성됩니다.
7. 대상을 포함하는 클래스 내부: builder() 메서드가 생성되며, 새로운 빌더 인스턴스를 생성합니다.
각 생성된 요소는 해당 요소가 이미 존재하는 경우(매개변수 개수는 무시하고 이름만 고려함) 자동으로 건너뛰게 됩니다. 이는 빌더 자체에도 적용됩니다. 해당 클래스가 이미 존재하는 경우, lombok은 이미 존재하는 이 클래스 내부에 필드와 메서드를 주입하기 시작합니다. 물론 주입할 필드/메서드가 이미 존재하는 경우 예외입니다. 그러나 다른 메서드(또는 생성자)를 빌더 클래스에 생성하는 lombok 주석을 넣을 수 없습니다. 예를 들어, 빌더 클래스에 @EqualsAndHashCode를 넣을 수 없습니다.
(공홈 doc 확인 중) ... 바로 이해가 안 되는데, 어쨌든 내 눈에는 getter, setter, tostring 하나 하나 따로 적용해서 클래스 인스턴스 만드는 것과 비슷한 결과물을 낳는다로 읽힌다.
@Builder는 컬렉션 매개변수/필드에 대한 'singular' 메서드를 생성할 수 있습니다. 이 메서드는 리스트 전체 대신 요소 1개를 받아와 리스트에 요소를 추가합니다. 예를 들어:
Person.builder()
.job("Mythbusters")
.job("Unchained Reaction")
.build();
위와 같이 사용하면 List 타입의 jobs 필드에 2개의 문자열이 포함됩니다. 이 동작을 얻으려면 필드/매개변수에 @Singular 주석을 달아야 합니다. 이 기능에 대한 자체 문서가 있습니다.
"메서드" 모드가 명확해지면, 생성자에 @Builder 주석을 넣는 것은 유사하게 작동합니다. 사실 생성자는 그들을 호출하는 특별한 구문을 가진 정적 메서드일 뿐입니다. '반환 타입'은 생성하는 클래스이며, 타입 매개변수는 클래스 자체의 타입 매개변수와 동일합니다.
마지막으로, @Builder를 클래스에 적용하는 것은 마치 @AllArgsConstructor(access = AccessLevel.PACKAGE)를 클래스에 추가하고 이 all-args 생성자에 @Builder 주석을 적용한 것과 같습니다. 이는 명시적인 생성자를 작성하지 않은 경우에만 작동합니다. 명시적인 생성자가 있는 경우 클래스에 @Builder 주석을 대신 생성자에 넣어야 합니다. 또한, 클래스에 @Value와 @Builder를 모두 넣으면, @Builder가 생성하려는 패키지 프라이빗 생성자가 우선되어 @Value가 생성하려는 생성자가 억제됩니다.
...
물건을 만들 때, 때로는 많은 정보나 속성이 함께 필요한 경우가 있어요. 그런데 이 모든 정보를 한 번에 제공하기는 어려울 때가 있어요. 예를 들어, 사람의 정보를 저장할 때 이름, 나이, 주소 등 여러 가지 정보가 필요한데, 이 모든 정보를 한 번에 입력하려면 복잡하고 어려울 수 있어요.
여기서 "빌더 패턴"이 도움을 줍니다. 빌더 패턴은 마치 레고 블록을 하나씩 쌓아올리듯이, 물건을 조금씩 만들어 나가는 방법이에요. 간단한 조각들을 조합하여 복잡한 것을 만들어내는 아이디어라고 생각하면 돼요.
예를 들어, 우리가 "사람"을 만든다고 생각해봅시다. 이름, 나이, 주소를 함께 입력해야 하는데요. 빌더 패턴을 사용하면 먼저 이름을 넣고, 그 다음에 나이를 넣고, 마지막으로 주소를 넣는 식으로 조금씩 추가해 나갈 수 있어요. 이렇게 조금씩 추가하면서 마치 빌딩 블록을 쌓듯이 정보를 넣어서 최종적으로 "사람"을 완성할 수 있게 되는 거죠.
이렇게 빌더 패턴을 사용하면 복잡한 정보를 조금씩 추가하면서 물건을 만들어갈 수 있어서 편리하고 쉽게 정보를 입력할 수 있습니다.
라고 chatGPT가 말했다.
@Builder를 사용하여 여러분이 만든 클래스의 인스턴스를 생성하는 빌더를 만들 때 (이것은 메서드에 @Builder를 추가하지 않는 한 항상 해당됩니다), @Builder(toBuilder = true)를 사용하여 이 클래스 내부에 toBuilder()라는 이름의 인스턴스 메서드도 생성할 수 있습니다. 이 메서드는 현재 인스턴스의 모든 값으로 시작하는 새로운 빌더를 만듭니다. 또한 매개변수(생성자 또는 메서드의 경우)나 필드(타입에 @Builder를 사용하는 경우)에 @Builder.ObtainVia 주석을 넣어서 해당 필드/매개변수의 값을 이 인스턴스에서 가져오는 대체 수단을 나타낼 수 있습니다. 예를 들어 메서드를 지정할 수 있습니다: @Builder.ObtainVia(method = "calculateFoo").
빌더 클래스의 이름은 FoobarBuilder입니다. 여기서 Foobar는 대상의 반환 타입의 간단화된 제목화된 형식입니다. 즉, 생성자와 타입에 대한 @Builder의 경우 여러분의 타입의 이름이 되며, 메서드에 대한 @Builder의 경우 반환 타입의 이름이 됩니다. 예를 들어, com.yoyodyne.FancyList라는 클래스에 @Builder가 적용되면 빌더 이름은 FancyListBuilder가 됩니다. 메서드에 @Builder가 적용되어 반환 타입이 void인 경우 빌더의 이름은 VoidBuilder가 됩니다.
빌더의 설정 가능한 측면은 다음과 같습니다:
빌더 클래스 이름 (기본값: 반환 타입 + 'Builder')
build() 메서드 이름 (기본값: "build")
builder() 메서드 이름 (기본값: "builder")
toBuilder() 사용 여부 (기본값: 미사용)
생성된 요소의 접근 레벨 (기본값: public).
(권장되지 않음) 'set' 메서드의 접두사를 변경하려는 경우, 즉, Person.builder().setName("Jane").build() 대신에 Person.builder().name("Jane").build()와 같이 사용하려는 경우와 설정값.
모든 옵션을 기본값에서 변경한 예제 사용법:
@Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true, access = AccessLevel.PRIVATE, setterPrefix = "set")
Jackson, JSON/XML 도구와 함께 빌더를 사용하고 싶나요? 걱정하지 마세요. @Jacksonized 기능을 확인해보세요.
여기서부터는 하나도 모르겠다.
https://projectlombok.org/features/Builder
이 수준부터는 doc을 읽는 게 나한테 도움이 되지 않을 거 같아서
실제 코드에서 배우려고 한다.
빌더 개념은 프로그래밍에서 복잡한 객체를 생성하고 초기화하는 방법을 설명합니다. 복잡한 객체란 여러 가지 속성이나 정보를 가지고 있는 객체를 말합니다. 이런 객체를 만들 때는 많은 정보를 한꺼번에 제공해야 할 때가 있는데, 이것이 어려울 수 있습니다.
빌더 패턴은 이러한 복잡한 객체를 단계별로 만들어 나가는 방법을 제공합니다. 이는 마치 레고 블록을 하나씩 조립하여 큰 구조물을 만드는 것과 비슷합니다. 빌더 패턴을 사용하면 객체를 조금씩 만들어 나갈 수 있어서 복잡한 정보를 조금씩 추가하면서 객체를 초기화할 수 있습니다.
빌더 패턴은 보통 다음과 같은 구조를 가집니다:
빌더 클래스: 객체를 조립하고 초기화하는 역할을 담당합니다. 이 클래스에는 객체의 속성을 설정하기 위한 메서드들이 정의되어 있습니다.
생성자: 빌더 클래스를 통해 객체를 생성하고 초기화합니다. 빌더 클래스의 메서드를 호출하여 객체의 속성을 설정하고, 최종적으로 생성자를 통해 객체를 완성합니다.
이렇게 빌더 패턴을 사용하면 복잡한 객체를 만들 때 코드를 더욱 읽기 쉽고 유지보수하기 쉽도록 만들어줍니다. 객체의 속성을 하나씩 추가하거나 변경하면서 객체를 초기화할 수 있어서 편리하게 사용할 수 있습니다.
빌더 패턴은 객체 생성과 초기화를 더욱 유연하고 가독성 있게 만들기 위한 디자인 패턴 중 하나입니다. 이 패턴은 복잡한 객체를 생성하고 그 객체의 속성을 설정하는 과정을 간결하게 다루기 위해 사용됩니다.
빌더 패턴을 사용하면 다음과 같은 이점을 얻을 수 있습니다:
가독성: 복잡한 객체 생성 코드가 한 곳에 모이기 때문에 코드를 이해하고 유지보수하기가 더 쉬워집니다.
유연성: 객체 생성과 초기화 과정을 단계별로 조작할 수 있어서 다양한 설정을 쉽게 구현할 수 있습니다.
객체의 불변성: 빌더 패턴은 객체의 속성을 설정한 후에 변경할 수 없도록 만들 수 있어서 불변 객체를 생성하는 데 도움이 됩니다.
일반적인 빌더 패턴의 구성은 다음과 같습니다:
빌더 클래스: 객체 생성 및 초기화를 담당하는 클래스입니다. 이 클래스에는 객체의 속성을 설정하기 위한 메서드들이 정의되어 있습니다.
생성자: 빌더 클래스를 통해 실제 객체를 생성하고 초기화합니다. 빌더 클래스의 메서드를 호출하여 객체의 속성을 설정하고, 최종적으로 생성자를 통해 객체를 완성합니다.
예를 들어, 사람 객체를 만들기 위해 이름, 나이, 성별 등의 정보를 설정해야 한다고 가정해봅시다. 빌더 패턴을 사용하면 이 정보를 조금씩 설정하면서 객체를 만들 수 있습니다. 이로써 가독성이 높아지고, 객체 생성 과정을 재사용하거나 변경하기가 용이해집니다.
간단하게 말해서, 빌더 패턴은 복잡한 객체를 조금씩 만들어가며 초기화하는 디자인 방법이며, 코드의 가독성과 유연성을 개선해줍니다.