본문 바로가기
JAVA/Effective Java

item 1. 생성자 대신 정적 팩토리 메소드를 고려하라

by Garonguri 2022. 4. 7.
728x90

* 정적 팩토리 메서드 (static factory method) ? 

: public 생성자와 별도로 해당 클래스의 instance를 반환하는 정적 메소드.  public 생성자 대신 or 같이 사용 가능.

 

* 장점

  1. 이름을 가질 수 있다. 
    • 메서드에 이름을 부여함으로써 반환될 객체의 특성을 잘 나타낼 수 있음.
    • 한 class 내에서 시그니처가 같은 생성자가 여러 개 필요한 경우에 사용
      1. 똑같은 type을 파라미터로 받는 생성자를 두개 이상 만들 수 없는 제약 조건이 있으므로, 이 경우에 사용하면 좋다.
      2. 즉, 매개 변수의 타입,개수가 같은 생성자도 만들 수 있다.
ublic static void main(String[] args) {

    Book book1 = new Book("Effective java");
    Book book2 = Book.BookWithName("Effective java2");
    Book book3 = Book.BookWithAuthor("Gayoung");

}

public static class Book{
    private String name;
    private String author;

    //public 생성자로 객체 초기화 하기
    public Book(String name) {
        this.name = name;
    }

    // static factory method로 초기화 하기
    public static Book BookWithName(String name){
        return new Book(name);
    }
    public static Book BookWithAuthor(String author){ return new Book(author);}
/* -> non-static variable cannot be referenced from a static context 에러가 나는 경우가 있다.

static 은 new() 명령으로 객체를 생성(인스턴스화)하지 않아도 바로 쓸 수 있다고 한다.
따라서 이미 인스턴스화되어 있기 때문에 에러가 나는 거라고 생각하면 될 것 같다.
간단히 클래스에 static을 써주는 것 만으로도 해결이 되었다. */
}

 

 

 

2. 호출 시마다 객체(인스턴스)를 새로 생성하지 않아도 된다.

  • 인스턴스를 미리 만들어놓거나 새로 생성한 인스턴스를 재활용한다.
  • 플라이웨이트 패턴
  • 정적 팩토리 방식의 클래스는 인스턴스 통제 클래스다.
    • 클래스를 singleton으로 만들 수 있다.
    • 클래스를 non인스턴스하게 만들 수 있다.
    • 불변 값 클래스에서  a==b일 때만 a.equals(b)가 성립되게 동치 인스턴스가 하나뿐임을 보장할 수 있다.
    • 열거 타입은 인스턴스가 하나만 만들어짐을 보장할 수 있다.

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

  • 반환할 객체의 클래스를 자유로이 선택할 수 있다. -> 유연성 증가
    • API의 부피를 작게 유지하는 데 도움이 된다. 
    • 구현된 클래스를 공개하지 않고도 객체를 반환할 수 있기 때문.
  • 반환 객체를 인터페이스로 사용하는 경우 : 인터페이스 기반 프레임워크
/*
java.util.Collections는 45개에 달하는 인터페이스의 non-public 구현체의 instance를 제공한다.
구현체를 return하므로 API노출을 줄일 수 있고, 따라서 public으로 제공해야 할 API를 줄였을 뿐 아니라
프로그래머의 구현 단계&난이도를 뜻하는 개념적인 무게(conceptual weight)까지 줄일 수 있다.
 */

4. 반환 타입의 하위 타입이기만 한다면, 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

  • 클라이언트는 정적 팩터리 메서드가 반환하는 객체가 어느 클래스의 인스턴스인지 알 필요가 없다.

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

  • 따라서, '서비스 제공자 프레임워크'를 만들 수 있게 됨. ex)JDBC
  • 서비스 제공자 프레임워크란? 서비스 인터페이스, 제공자 등록 API, 서비스 접근 API 총 3개의 컴포넌트로 이루어진 프레임워크.
  • 이 컴포넌트 중 서비스 접근 API가 5번, 즉 '유연성'을 통해 만들 수 있게 된다.
서비스 제공자 프레임워크 ex) JDBC
   1. 서비스 인터페이스 -> Connection
       : 구현체의 동작을 정의함.
   2. 제공자 등록 API -> DriverManager.registDriver
       : 제공자가 구현체를 등록할 때 사용함.
   3. 서비스 접근 API -> DriverManager.getConnection
       : 클라이언트가 서비스의 인터페이스를 얻을 때 사용함.
      서비스 접근 API를 사용할 때 원하는 구현체의 조건 명시 가능.
      (조건 명시하지 않을 시 기본 구현체 또는 지원하는 구현체를 돌아가며 반환함)
      -> 이것이 유연한 정적 팩터리.

   +(4. 서비스 제공자 인터페이스) -> Driver
      : 서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체.
      
    즉, 반환할 객체를 펙토리 메서드 방식을 이용하여 만들기 때문에, 서비스 제공자 프레임워크( Ex)JDBC) 에서도 
    각 상황에 따라 팩토리 메서드의 내용만 바꿔서 필요한 객체를 얻을 수 있다.

 

* 단점

1. 정적 팩터리 메소드만 제공한다면 하위 클래스를 만들 수 없다. 

  • 상속을 하기 위해서는 public 또는 protected 생성자가 필요하기 때문이다.
  • (그러나, 불변 클래스/불변 타입으로 만들기 위해서는 상속보다 컴포지션을 사용해야 하는 제약이 있기 때문에 이 관점에서는 유리하다.)
  • 즉, java.util.Collections로 만든 구현체는 상속할 수 없다.

2. 프로그래머가 찾기 어렵다.

  • API 명세서, 메서드 이름 등을 통해 그나마 찾기 쉽게, 알기 쉽게 해주어야 한다.

 

* 정적 팩토리 명명 방식

1. from : 매개변수를 하나 받아 해당 타입의 인스턴스 반환.  (형 변환)

2. of : 여러 가지 매개변수를 받아 적합한 타입의 인스턴스로 반환.

3. valueof : from+of

4. getInstance : 매개변수를 받을 시 매개변수로 명시한 인스턴스 반환. 

5. create/newInstance : instance/getInstance 같지만, 매번 새로운 인스턴스를 생성해 반환.

6. getType : getInstance와 같지만, '다른'클래스의 팩터리 메서드 정의 시 사용. 

7. newType : newInstance와 같지만, '다른'클래스의 팩터리 메서드 정의 시 사용.

8. type : getType/newType

*  public 생성자가 쓰기 쉽고 직관적이라 많이 사용했지만,
*  다양한 장점을 가진 정적 팩터리를 사용하는게 유리한 경우가 많다.
*
*  정적 팩터리를 사용해보자!

 

728x90

댓글