업데이트:

Effective Java를 1회차를 독파하였고, 중요하다 생각하는 내용을 정리하여 포스팅하려 합니다.

그 첫 번째는 객체 생성 패턴입니다.

📌 모든 소스는 Github에 있습니다.

제가 경험한 바로 객체(주로 Domain 객체겠죠?)를 생성, 값을 바인딩하는 방법은 총 4가지로 나뉩니다.

  1. 기본 객체 생성자를 통해 객체 생성 → Setter 메소드를 통해 객체 값 바인딩
  2. 필수값, 옵셔널한 값에 따라 여러 개의 생성자를 두어 필요할때 마다 사용
  3. Static Factory Method 사용 → Effective Java 내용
  4. Builder 패턴 사용 → Effective Java 내용

기본 생성자, Setter

기본 생성자와 Setter를 이용한 코드는 다음과 같습니다.

Post post = new Post();
post.setTitle("title");
post.setAuthor("author");
post.setContent("content");

아직도 많은 회사들이 위의 코드를 사용하는 것을 볼 수 있는데, 이 패턴은 안좋은 이유가 몇 가지 있습니다.

  1. 기본 생성자 접근을 오픈함으로써 무분별한 객체 생성을 막을 수 없습니다.
  2. 객체가 가져야할 필수 값을 누락할 소지가 있습니다.

❗그리고 Setter의 안좋은 점을 그대로 가져오는데, Setter의 안좋은 점은 다른 포스팅에서 다루도록 하겠습니다.

여러 개의 생성자

public Post(String title) {
      new Post(null, title, null, null);
  }

  public Post(String title, String content) {
      new Post(null, title, content, null);
  }

  public Post(String title, String content, String author) {
      new Post(null, title, content, author);
  }

  public Post(Long id, String title, String content, String author) {
      this.id = id;
      this.title = title;
      this.content = content;
      this.author = author;
  }

이 패턴은 다음과 같은 단점을 가지고 있습니다.

  1. 유지보수 포인트가 늘어납니다.
  2. 데이터 타입이 동일 시 되는 변수를 parameter로 넣을 때, 혼동이 올 수 있습니다. (요새는 IDE가 잘 보여주긴 합니다만)

Static Factory Method

public static Post ofTitle(String title) {
    Post post = new Post();
    post.title = title;
    return post;
}

public static Post ofTitleAndContent(String title, String content) {
    Post post = new Post();
    post.title = title;
    post.content = content;
    return post;
}

장점

  1. 메서드가 이름을 가질 수 있습니다.
  2. Simple하고 명확하게 사용 가능합니다.
  3. 다양하게 사용 가능합니다.
    1. 객체의 타입 변환하는 방향으로 메소드를 짤 수 있습니다.
      1. DtoEntity

단점

  1. 생성자가 없을 수 있어 상속받은 Class는 만들 수 없습니다.
  2. 프로그래머에게 인지가 잘 되지 않을 수 있습니다.

네이밍 관용어구

  • from : 하나의 매개 변수를 받아서 객체를 생성
  • of : 여러개의 매개 변수를 받아서 객체를 생성
  • getInstance  instance : 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
  • newInstance  create : 새로운 인스턴스를 생성
  • get[OtherType] : 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
  • new[OtherType] : 다른 타입의 새로운 인스턴스를 생성.

출처 : https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/

Builder 패턴

객체 생성을 담당하는 Builder를 만들고 그 Builder에 값을 바인딩하여 객체를 반환하는 패턴입니다.

public class Car {

    private String name;
    private String number;
    private Integer size;

    public Car(CarBuilder carBuilder) {
        this.name = carBuilder.name;
        this.number = carBuilder.number;
        this.size = carBuilder.size;
    }

    public static CarBuilder builder(String name, String number) {
        return new CarBuilder(name, name);
    }

    public static class CarBuilder {
        private final String name;
        private final String number;
        private Integer size = 0;

        public CarBuilder(String name, String number) {
            this.name = name;
            this.number = number;
        }

        public CarBuilder size(Integer val) {
            this.size = val;
            return this;
        }

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

강의를 통해 작성한 코드입니다.

CarBuilder builder = Car.builder("name", "number");
builder.size(0);
Car build = builder.build();

사용하는 방법은 먼저 CarBuilderbuilder() 메소드를 통해 생성합니다. 이때 필수값인 name, number를 인자로 받고, 이 CarBuilder를 통해 나머지 값인 size를 바인딩 합니다.

장점

  1. 상속받은 ClassBuilder가 정의한 builder()메서드가 아닌 상위 클래스 타입을 반환하는 것이 아닌 자신의 타입을 반환 합니다.

단점

  1. Builder를 반드시 생성해야 하기 때문에 추가 생성비용이 무조건 필요합니다.
  2. 인자의 갯수가 적다면 일반 생성자를 사용하는 것이 코드의 라인 수를 더 줄이고, 사용하기 편한 방법이라 생각합니다.

💡Builder 코드를 직접 짠다면 유지보수할 포인트가 또 늘어나게 되겠지만 Lombok@Builder 어노테이션을 사용하여 최대한 간단하게 사용하는게 좋을 것 같습니다.

Lombok@Builder 어노테이션 활용법에 대해선 다른 포스팅에서 다뤄보도록 하겠습니다.

마무리

객체를 생성하는 여러가지 방법에 대해서 다뤄봤습니다.

개인적으로 Setter를 지양하고 static factory methodBuilder 패턴이 자주 사용되어야 한다 생각합니다. (일반 생성자는 상황에 따라~)

고려해야 할 상황이 하나 더 있는데, 바로 비즈니스 로직의 위치입니다.

비즈니스 로직의 위치가 Domain이 아닌 Service Layer에 있다면 Setter를 활용 했을 확률이 높습니다.

즉, DDD가 아닐 경우입니다. 이럴때 static factory methodBuilder를 사용한다 한들 불변객체를 만들지 못하기 때문에 그 효과는 절반으로 줄게 될 것입니다. 결국 DDD로 바꿔야 한다는 것인데, 아직 Setter 사용에 익숙한 회사들에서 기존 코드를 모두 리팩토링 하면서 static factory methodBuilder로 교체하면서 기술 부채를 덜어내는 수고를 들일지가 미지수네요.

객체 생성 패턴에 대한 얘기는 여기서 마무리하고 다음은 Setter를 지양해야 하는 이유와 @Builder에 대해서 다뤄보도록 하겠습니다.

여기까지 제 포스트를 읽어주셔서 감사합니다.

항상 건강 조심하시고 행운이 가득하시길 바랍니다~ 👍

댓글남기기