TIL 2021.02.19

🧐 TIL (Today I Learned)

1. 정적 팩토리 메서드와 생성자를 통한 객체 생성

미션의 요구사항에서는 다음과 같이 설명되어있다.

Pieces는 값 (value) 객체여야 한다. 즉, private 생성자를 가져야한다. 인스턴스를 생성한 이후에는 인스턴스의 상태 값을 변경할 수 없어야 한다.

이 부분이 마치 무조건 private 생성자를 가져야하는 것처럼 들리는데 아래와 같이 public 생성자를 가져도 Value Object를 만들 수 있다

final class Name {
    private String value;

    public Name(String value) {
        this.value = value;
    }
}

실제로 IDE에서 인스턴스를 만들어서 value에 접근하려고하면 아래와 같이 에러난다.

왜 굳이 정적 메서드를 사용하라고 했을까? 그냥 정적메서드가 아니라 생성자가 private이니까 정적 팩토리 메서드 쓰라고 하는 뜻이다.

생성자 VS 정적 팩토리 메소드

String str = new String("kyu");
String str = String.valueOf("kyu");

기본적으로 둘을 코드로 사용할 때는 위와 같은 차이를 가진다. 두번쨰코드는 String의 valueOf() 라는 정적 메소드로 kyu 라는 문자열을 생성한 것이다.

언제 무엇을 사용해야 할까?

생성자를 통하지 않고 정적 팩토리 메서드를 사용하는 이유는 뭘까? 이펙티브 자바 1장에는 “Consider static factory methods instead of constructors” 라고 나온다고 한다. 그만큼 어떤 이점이 있기 때문에 정적 팩토리 메서드를 사용하는 것이다. 그렇다고 앞으로 생성자 대신에 전부 정적 팩토리 메서드를 사용하라는 말은 아닐 것이다.

정적 팩토리 메서드 이해하기

// 코드 1

public class User {
    
    private final String name;
    private final String email;
    private final String country;
    
    public User(String name, String email, String country) {
        this.name = name;
        this.email = email;
        this.country = country;
    }
    
    // standard getters / toString
}

코드 1은 그냥 일반적으로 생성자로 데이터를 받아서 객체를 생성하는 방식이다. 딱히 문제가 없어보인다.

그런데 country만 기본값으로 모두 Korea 를 가지게 하고 싶다면 어떨까? country를 상수로 바꾸고 생성자를 수정하던지, 애초에 생성자에 Korea 라고 붙이던지 어쨋든 리팩토링이 필요할 것이다.

아니면, 이때 코드 2처럼 정적 팩토리 메서드를 사용할 수 있다.

// 코드 2

public static User createWithDefaultCountry(String name, String email) {
    return new User(name, email, "Korea");
}

그리고 이 정적 팩토리 메소드를 사용해서 country 값이 Korea 로 할당된 객체를 다음 코드 3처럼 생성할 수 있다.

// 코드 3

User user = User.createWithDefaultCountry("Kyu", "[email protected]");

정적 팩토리 메서드 - 생성자 밖으로 로직을 옮기기

코드 1에서 봤던 User 클래스에서 어떤 기능을 구현하기 위해 생성자에 로직을 넣도록 요구되는 그런 일이 발생한다면, User 클래스는 결함이 있는 디자인으로 빠르게 썩어갈 것이다.

구체적으로 그런 일은 어떤 일인가? 예를 들어서 User 객체가 생성됐을 때마다, 생성된 시각을 기록하는 기능을 추가해주고 싶다면 어떨까?

만약에 생성자에 그 로직을 넣는다면 단일 책임 원칙을 어기게 된다. 단지, 필드를 초기화해주는 작업이 아니라 그보다 훨씬 더 많은 일을 하는 로직을 넣어줌으로써 굉장히 단단하게 결집되어 있는 생성자를 만들게 될 것이다.

이 때, 정적 팩토리 메서드를 사용해서 이 문제를 해결할 수 있다.

// 코드 4

public class User {
    
    private static final Logger LOGGER = Logger.getLogger(User.class.getName());
    private final String name;
    private final String email;
    private final String country;
    
    // standard constructors / getters
    
    public static User createWithLoggedInstantiationTime(
      String name, String email, String country) {
        LOGGER.log(Level.INFO, "Creating User instance at : {0}", LocalTime.now());
        return new User(name, email, country);
    }
}

정적 팩토리 메서드로 만들면서 생성자에 직접 로직을 넣는 형식보다 느슨한 형태의 결합 코드를 가지게 됐다.

다음 코드 5처럼 인스턴스를 생성할 수 있을 것이다.

// 코드 5

User user 
  = User.createWithLoggedInstantiationTime("Kyu", "[email protected]", "Korea");

참고
Java Constructors vs Static Factory Methods


📚일기

담부터 스크럼 할 때, 개념적인 이야기를 할 때는 처음에 그 개념이 간단하게 짚고 넘어가야겠다. 상대방이 알고있는 개념인지 참고해서 대화를 해야지.


✅투-두

내일도 미션 4 진행.

나중에 해야 할 To-do 링크

좋은 웹페이지 즐겨찾기