(인프런) 코딩으로 학습하는 GoF의 디자인 패턴 - 백기선, 강의를 보고 정리한 글입니다.
코드는 GitHub 에 있습니다
#1. 객체 생성 관련 패턴 |
#2. 구조 관련 패턴 |
#3. 행동 관련 패턴 |
||
✔️ 빌더 패턴
- 동일한 프로세스를 거쳐 다양한 구성의 인스턴스를 만드는 방법
- 멤버변수가 많고, 생성자가 많아질 경우 복잡하고 파악하기 힘든 생성과정이 생기는 것이므로, 이를 순차적이고 선언적인 객체 생성 프로세스로 바꾸는 방법입니다.
→ 빌더 패턴을 사용하면 (복잡한) 객체를 만드는 프로세스를 독립적으로 분리할 수 있다.
✔️ 빌더 패턴 구현 방법
- 빌더 패턴 구현의 요점은, 메소드 체이닝 입니다.
- 메소드 체이닝으로 엔티티의 데이터를 세팅해주는 빌더라는 인터페이스를 만들어 준 후 계속 스스로를 반환하고, 마지막에 해당 객체를 반환토록 하는 것입니다.
객체
- 아래와 같이 여러 멤버변수를 가지는 TourPlan 이라는 객체와 생성자가 존재합니다.
public class TourPlan {
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
public TourPlan() {
}
public TourPlan(String title, int nights, int days, LocalDate startDate, String whereToStay,
List<DetailPlan> plans) {
this.title = title;
this.nights = nights;
this.days = days;
this.startDate = startDate;
this.whereToStay = whereToStay;
this.plans = plans;
}
}
- 조금 비약해서 이 객체를 생성하고 데이터를 세팅해주면 아래으 사진처럼, 굉장히 복잡한 프로세스르 거쳐야합니다.
⬇️
- 이 구조를 이제 빌더 패턴을 적용해서 아래와 같은 구조로 변경해 보겠습니다.
- APP : 클라이언트의 역할을 하는 객체라고 보시면 됩니다.
- TourDirector : 빌더패턴을 적용할 TourPlanBuiler 를 목적에 맞게 미리 구현해둔 메소드의 집합을 가지는 객체입니다. (선택적이라고 생각, TestFixture 와 그 개념과 목적이 비슷)
- TourPlanBuilder : 빌더 패턴 적용한 interface
- DefaultTourPlanBuilder : TourPlanBuilder 의 구현체
TourPlanBuilder
- 먼저 스스로를 반화하는 기능과 객체를 반환하는 메서드를 정의한 인터페이스로 빌더의 뼈대를 잡아야합니다.
- 그리고 하위 구현체에서 데이터를 세팅해줍니다.
public interface TourPlanBuilder {
//메서드 체이닝 사용 → 인터페이스에서 정의한 또 다른 기능을 사용하기 위해서
TourPlanBuilder title(String title);
TourPlanBuilder nightsAndDays(int nights, int days);
TourPlanBuilder startDate(LocalDate localDate);
TourPlanBuilder whereToStay(String whereToStay);
TourPlanBuilder addPlan(int day, String plan);
//getPlan 을 호출하기 전가지는 계속 체이닝 사용 (마지막 단계이기에 검증하기 좋은 위치)
TourPlan getPlan();
}
public class DefaultTourBuilder implements TourPlanBuilder{
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
@Override
public TourPlanBuilder nightsAndDays(int nights, int days) {
this.days = days;
this.nights = nights;
return this;
}
//생략
@Override
public TourPlanBuilder addPlan(int day, String plan) {
if (this.plans == null) {
this.plans = new ArrayList<>();
}
this.plans.add(new DetailPlan(day, plan));
return this;
}
@Override
public TourPlan getPlan() {
return new TourPlan(title, nights, days, startDate, whereToStay, plans);
}
}
⬇️
Client
- 이제 빌더패턴을 적용해서 객체를 생성할 수 있습니다.
- 빌더패턴을 적용하면 선언적이고, 순차적인 구조로 객체 생성의 프로세스를 작성할 수 있습니다.
public class App {
public static void main(String[] args) {
TourPlanBuilder builder = new DefaultTourBuilder();
TourPlan plan = builder.title("칸쿤여행")
.nightsAndDays(2, 3)
.startDate(LocalDate.of(2020, 10, 2))
.whereToStay("hotel")
.addPlan(0, "저녁 식사")
.addPlan(1, "아침 식사")
.getPlan();
TourPlan longBeachTrip = builder.title("롱비치")
.startDate(LocalDate.now())
.getPlan();
}
}
- 📌 또한 빌더패턴을 이용해서 초기 Builder 를 선언하는 부분을 이용한다면 파라미터를 이용해 강제적으로 데이터 세팅이 가능하도록 설계도 할 수 있습니다.
public class App {
public static void main(String[] args) {
TourPlan tourPlan = DefaultTourBuilder.Builder("title").getPlan();
}
}
public class DefaultTourBuilder implements TourPlanBuilder{
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
public static TourPlanBuilder Builder(final String title) {
return new DefaultTourBuilder().title(title);
}
//생략
}
✔️ Lombok @Builder 가 생성하는 코드
- Lombok 은 많은 개발자분들이 애용하는 어노테이션을 제공하는 라이브러리 입니다.
- Lombok 이 제공하는 어노테이션 중 @Builder 가 존재하는데 이 빌더 어노테이션은 빌더패턴을 어떻게 적용하는지 살펴 보았습니다.
@Builder
public class TourPlan {
이렇게 겍체에 @Builder 를 붙이고 build를 돌리면, 컴파일 된 파일이 'build' 혹은 'target'폴더에 생성됩니다.
Lombok @Builder Code
- Lombok 은 리플렉션을 이용해서, 컴파일 시 해당 어노테이션이 등록된 위치에 맞춰, 정의된 기능을 주입시킵니다.
- @Builder 가 적용된 객체는 위에서 살펴보았던 빌더패턴이 그대로 객체 내부 코드에 삽입된 걸 확인해 볼 수 있었습니다.
public class TourPlan {
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
public TourPlan() {
}
public TourPlan(String title, int nights, int days, LocalDate startDate, String whereToStay, List<DetailPlan> plans) {
this.title = title;
this.nights = nights;
this.days = days;
this.startDate = startDate;
this.whereToStay = whereToStay;
this.plans = plans;
}
public static TourPlanBuilder builder() {
return new TourPlanBuilder();
}
public static class TourPlanBuilder {
private String title;
private int nights;
private int days;
private LocalDate startDate;
private String whereToStay;
private List<DetailPlan> plans;
TourPlanBuilder() {
}
public TourPlanBuilder title(final String title) {
this.title = title;
return this;
}
public TourPlanBuilder nights(final int nights) {
this.nights = nights;
return this;
}
public TourPlanBuilder days(final int days) {
this.days = days;
return this;
}
public TourPlanBuilder startDate(final LocalDate startDate) {
this.startDate = startDate;
return this;
}
public TourPlanBuilder whereToStay(final String whereToStay) {
this.whereToStay = whereToStay;
return this;
}
public TourPlanBuilder plans(final List<DetailPlan> plans) {
this.plans = plans;
return this;
}
public TourPlan build() {
return new TourPlan(this.title, this.nights, this.days, this.startDate, this.whereToStay, this.plans);
}
public String toString() {
return "TourPlan.TourPlanBuilder(title=" + this.title + ", nights=" + this.nights + ", days=" + this.days + ", startDate=" + this.startDate + ", whereToStay=" + this.whereToStay + ", plans=" + this.plans + ")";
}
}
}
그럼
끝!
'Java > Design Pattern' 카테고리의 다른 글
[디자인 패턴] 구조 패턴 - 어댑터 패턴 (Adapter Patterns) (0) | 2023.02.16 |
---|---|
[디자인 패턴] 생성패턴 - 프로토타입 패턴 (Prototype Patterns) (0) | 2023.02.15 |
[디자인 패턴] 생성패턴 - 추상 팩토리 패턴 (Abstract Factory patterns) (0) | 2023.02.09 |
[디자인 패턴] 생성패턴 - 팩토리 메소드 패턴이란 (Factory Method patterns) (1) | 2023.02.08 |
[디자인 패턴] 생성패턴 - 싱글톤 패턴이란 (Singleton patterns) (0) | 2023.02.08 |