[리팩토링]빌더패턴을 활용해 객체 생성하기

스프링MVC로 만들어진 기존 서비스단 코드에 아래와 같이 매개변수 별로

동일객체를 만드는 코드가 있었다.

아래는 알림객체인 Notification을 만들어 전송하는 코드이다.

public void createNotificationByCase1(int a, int b, int c, String d, String f) {
    Notiifcaiton notification = new Notificaiton(a, b, c, d, f);
    sendAlarm(notification);
}
public Notification createNotificationByCase2(int a, int b, String d, String e) {
    Notiifcaiton notification = new Notificaiton(a, b, d, e);
    sendAlarm(notification);
}
public Notification createNotificationByCase3(int a, int b, int c, String d) {
    Notiifcaiton notification = new Notificaiton(a, b, c, d);
    sendAlarm(notification);
}

메서드Case1 ~ Case3별로 각기 다른 매개변수를 가지고 Notification 객체를 만든다.

그리고 Notification 클래스 내부는 아래와 같다.

public class Notification {
    private int a;
    private int b;
    private int c;
    private String d;
    private String e;
    private String f;

    Notification(int a, int b, int c, String d, String e, String f) {
       this.a = a;
       this.b = b;
       this.c = c;
       this.d = d;
       this.e = e;
       this.f = f;
     }
     Notification(int a, int b, int c, String d, String f) {
         this(a, b, c, d, null, f);
     }
     Notification(int a, int b, String d, String e) {
         this(a, b, 0, d, e, null);
     }
     Notification(int a, int b, int c, String d) {
         this(a, b, c, d, null, null);
     }
}

Notification 클래스 필드 6개 중, Notification 객체를 만드는 데 필요한 필수 멤버변수는

a, b, d고, 나머지 c, e, f는 선택 멤버변수이다.

위 Notification 클래스의 생성자를 보면 값이 없는 건 기본값(0 혹은 null)을 넣어서

다른 생성자를 호출하는 것을 알 수 있다.

위의 예제는 샘플코드를 만드느라 멤버변수를 6개만 넣었지만, 실제 리팩토링 한 코드의

클래스 멤버변수는 총 12개였고, 필수와 선택 멤버변수가 각각 6개씩이었다.

이런 상황하에 각 서비스에서 알림객체를 만들어 전송하려고 notificationService에 각기

다른 매개변수를 가진 메서드(Case1 ~ Case3 메서드)를  호출한 것이다.

notificationService를 이용하는 서비스단의 코드는 아래와 같다.

notificationService.createNotificationByCase1(a, b, c, d, f);

위와 같이 알림객체를 생성할 경우, 새로운 서비스에서 알림서비스를 사용할  때

또 다른 매개변수를 가진 메서드를 알림서비스에 만들어야 할 것이고, 그것을 이용하는

각 서비스들의 코드는 이해할 수 없는 매개변수의 나열로 가독성이 매우 나빠질 것이다.

타인은 물론 본인도 추후에 해당코드를 유지보수 할 경우, 어떤 데이터가 어디에 들어가

있는지 정확히 기억하기도 힘들고, 생성자 매개변수가 많으면 많을수록 그 복잡도는

매개변수 갯수에 비례해 증가하게 된다.

결국, 유지보수 시간이 기하급수적으로 늘게돼 나중에는 쳐다보기도 싫은

코드로 전락해버릴지도 모른다.

그래서, 이 복잡한 객체생성코드를 빌더패턴을 사용해서 리팩토링 하기로 했다.

빌더패턴을 사용해 객체생성을 하는 코드는 아래와 같다.

public class Notification {
    private int a;
    private int b;
    private int c;
    private String d;
    private String e;
    private String f;

    private Notification(Builder builder) {
        this.a = builder.a;
        this.b = builder.b;
        this.c = builder.c;
        this.d = builder.d;
        this.e = builder.e;
        this.f = builder.f;
     }

     public static class Builder {
        // Require Fields
        private int a;
        private int b;
        private String d;

        // Option Fields
        private int c;
        private String e;
        private String f;

        public Builder(int a, int b, String d) {
            this.a = a;
            this.b = b;
            this.d = d;
        }

        public Builder c(int c) {
            this.c = c;
            return this;
        }

        public Builder e(int e) {
            this.e = e;
            return this;
        }

        public Builder c(int f) {
            this.f = f;
            return this;
        }

        public Notification build() {
            return new Notification(this);
        }
    }
}

빌더패턴을 사용하고 난 이후에는 Notification클래스에 각 매개변수별로 만들어져 있던

생성자가 사라지고, Builder클래스의 build메서드를 통해 단일창구로 Notification객체를

만들게 되었다.

그리고 각 서비스에서 notificationService에 전달할 Notification 객체를 빌더패턴을

사용해 생성하고 아래와 같이 전달한다.

Notification notification = new Notification.Builder(a, b, d).c(c).f(f).build();
Notification notification = new Notification.Builder(a, b, d).e(e).build();
Notification notification = new Notification.Builder(a, b, d).d(d).build();
notificationService.sendNotification(notification);

Notification객체를 생성하는 필요한 필수 멤버변수인 a, b, d는 Builder클래스의 생성자를

통해 전달하고, 나머지 선택 멤버변수인 c, e, f는 set메서드 역할을 하는 메서드를 사용해

필요할 때 호출해서 값을 세팅하고 마지막에 build메서드를 호출해 Notification 객체를

생성하게 된다.

이로 인해, Notification 객체를 생성해 전송하던 notificationService의 각기 다른

매개변수를 가지던 메서드는 사리지고, Notificaiton객체 하나만을 받는

메서드 하나로 리팩토링 되었다.

긴 매개변수를 받던 메서드들이 사라지고, Notification 객체 하나만을 받도록 리팩토링 한

아래 코드를 보면 전과 비교해 코드 가독성이 월등히 좋아졌음을 알 수 있다.

public void sendNotification(Notification notification) {
    //기존 Notification 객체생성부분 삭제
    sendAlarm(notification);
}

빌더패턴을 사용해 객체 생성부분을 리팩토링해서 얻은 결과는 아래와 같다.

  • 각기 다른 매개변수별로 중구난방으로 만들어져있던 notificationService의 메서드를 Notification객체를 받는 단 하나의 메서드로 통일시킴
  • Notification클래스에 각기 다른 매개변수별로 만들어져있던 생성자를 Builder객체를 이용해 객체를 만드는 방법으로 통일시킴
  • 각 서비스별로 notificationService의 각기 다른 매개변수들을 가진 메서드를 호출하고 있던 것을 빌더패턴을 이용해 Notification객체를 생성하고 Notification객체를 받는 하나의 메서드를 호출하는 것으로 통일시킴

무엇보다 제일 좋은 것은 리팩토링으로 얻은 코드 가독성이다.

전에는 쳐다보기조차 싫었던 코드가 빌더패턴을 사용해 객체생성부분을 수정함으로써,

다음에 또 수정사항이 생겨도 빠른 시간안에 대처할 수 있는 여력이 생겼다는 것이다.

역시나 일상 속에 지속적인 리팩토링과 이를 뒷받침하는 테스트코드의 중요함을 다시

한번 느끼는 하루였다.

혹시나, 빌더패턴에 대해 궁금하신 분은 Effective java 항목2를 읽어보면 많은

도움이 될 것이다.

Advertisements

[리팩토링]빌더패턴을 활용해 객체 생성하기”에 대한 2개의 생각

  1. 다른 빌더 패턴에 대한 설명글들을 볼 때는 이게 뭔 소리지 싶었는데, 코드를 통해서 확실히 이해시켜주시네요. 좋은글 감사합니다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

w

%s에 연결하는 중