아이템 1. Constructor 대신 Static Factory Method 를 고려하라

객체를 생성하는 방법 (인스턴스화)

  1. public 생성자
  2. public static 팩토리 메소드

정적 팩터리 메서드가 생성자 보다 좋은 장점 5가지

생성자 : 생성자가 제공하는 파라미터와 그 반환 객체를 잘 설명하지 못할 수 있음

⇒ 정적 팩터리 메서드 : 잘 만든 이름을 가질 수 있도록 만들 수 있다.

public class Foo {

    String name;

    public Foo(String name) {
        this.name = name;
    }

    public static Foo withName(String name) { // 이름을 가질 수 있다.
        return new Foo(name);
    }

    public static void main(String[] args) {
        Foo foo = new Foo("hyungwoo");

        Foo foo1 = Foo.withName("hyungwoo");
    }
}

2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.

불변(immutable) 클래스 이거나, 매번 새로운 객체를 만들 필요가 없는 경우

⇒ 미리 만들어 둔 인스턴스 or 캐시해둔 인스턴스를 반환한다.

package item01;

public class Foo {

    public Foo() {
    }

    private static final Foo GOOD_NIGHT = new Foo(); // 미리 만들어둔 인스턴스

    public static Foo getFoo() {
        return GOOD_NIGHT;
    }

    public static void main(String[] args) {
        Foo foo2 = Foo.getFoo(); // 미리 만들어 둔 인스턴스 반환        
    }
}

3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.

public interface FooInterface {
  public static Foo getFoo() {
      return new Foo();
  }
}

이렇게 구현체를 인터페이스 안에서 반환 시키는 메서드를 만들 수 있음. (Foo가 FooInterface를 implements했다고 가정)

클래스에서 만들어줄 객체의 클래스를 선택 할 수 있는 유연함이 있다.

⇒ ex) 리턴 타입을 인터페이스로 지정하고 그 인터페이스의 구현체는 API로 노출 시키지 않으면서 그 구현체의 인스턴스를 리턴 타입으로 반환할 수 있다.

자바 8 이전 : 인터페이스에 public static 메소드 추가 제한

⇒ 인터페이스에 인스턴스화 불가한 동반 클래스를 만들어 그 안에 정적 메서드로 제공 (java.util.Collections가 대표적인 예)

java.util.Collections 가 45개 클래스를 공개하지 않고 제공 ⇒ 클라이언트는 인터페이스만으로 코딩 가능.

자바 8 이후 : 인터페이스에 public static 메소드 추가 가능

⇒ 인터페이스와 함께 동반 클래스를 만들 필요 없이 인터페이스에 public static 메소드를 추가하면 됨.

⇒ 하지만 인터페이스의 public static 메소드를 구현하기 위한 코드중 default 클래스에 둬야 하는 경우가 있음. (이부분 다시 읽어보기)

참고 : private static이 필요한 이유 ⇒ private 메서드가 있어야 하는 이유와 동일 ⇒ scope의 문제 ⇒ private이 말그대로 그 클래스 밖에서 쓰이기 싫듯이, private static은 static 메서드에서 외부에 공개 되지 않은 메서드(private static)을 사용하고 싶을때 필요함.


4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

장점 3의 연장선상이다.

public class Foo {

    String name;

    public Foo() {
    }

    public static Foo getFoo(boolean flag) {
        return flag ? new Foo() : new BarFoo(); // 매개 변수에 따라 다른 클래스 객체 반환
    }

    public static void main(String[] args) {
        Foo foo = Foo.getFoo(false);
    }

    static class BarFoo extends Foo {
    }
}

EnumSet 클래스의 경우, public static 메소드인 allOf(), of()를 제공함

⇒ 이 메소드의 리턴 타입은 enum 타입의 갯수에 따라 RegularEnumSet or JumboEnumSet으로 달라진다. (구현체 반환이 다름)

⇒ 이 구현체들은 숨겨둔 것들이기 때문에 클라이언트는 몰라도 됨.

public class Foo {

    String name;

    public Foo() {
    }

    public static void main(String[] args) {
        EnumSet<Color> colors = EnumSet.allOf(Color.class);
        EnumSet<Color> blueAndWhite = EnumSet.of(RED, WHITE);
                // Enum 갯수에 따라 인스턴스가 달라진다.
                // 싱글톤인지는 테스트 해봐야함
    }

    enum Color {
        RED, BLUE, WHITE
    }
}

5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

쉽게 말해서 정적팩터리 메서드 리턴타입이 인터페이스(클래스가 존재하지 않음) 여도 된다는 거임. 결국 3, 4 장점의 연장선상 이다. 리턴타입은 인터페이스지만 실제로 리턴을 구현체로 리턴해도 되기 때문이다.

public interface FooInterface {
}

public class Foo implements FooInterface {

    public static FooInterface getFoo() {
        return new Foo();
    }
}

댓글

Designed by JB FACTORY