본문 바로가기

Java/effectiveJava

[이펙티브자바] 1. 생성자 대신 정적팩터리 메서드를 고려하라

반응형

객체의 생성과 파괴

객체를 만들어야 할 때와 만들지 말아야 할 때를 구분하는법

올바른 객체생성방법과 불필요한 생성을 피하는 방법

제때 파괴됨을 보장하고 파괴 전에 수행해야할 정리 작업을 관리하는 요령을 알아본다.

 

public class Test {
    public static Test getTestInstance(){
        return new Test();
    }
}

정적 팩터리 메서드를 이용하여 Test클래스의 인스턴스를 가져올 수 있다.

일반적으로 클래스를 인스턴스화 할때 사용하는 생성자(new Test)를 사용하지 않고 정적 팩터리 메서드를 이용하면서 발생하는 장점과 단점을 알아보자.

 

장점

  1. 이름을 가질 수 있다.
    • public class Person {
          public Person(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public Person(String name) {
              this.name = name;
          }
      
          public Person(int age) {
              this.age = age;
          }
      
          private String name;
          private int age;
      
          public static Person PersonWithNameAndAge(String name, int age){
              return new Person(name, age);
          }
      }
       
    •  BigInteger(int, int, Random) vs BigInteger.probablePrime(int, Random)
    • 생성자에 들어가는 매개변수만 보고 무엇을 하는지 예상하는 것 보다는 PersonWithNameAndAge 메서드가 더 명확하게 이름과 나이가 있는 인스턴스를 만들어 준다는 것을 쉽게 알 수 있다.
  2. 호출될 때마다 새로운 인스턴스를 생성하지 않아도 된다.
    • public class Person {
          private static Person person;
          private String name;
      
          public Person(String name) {
              this.name = name;
          }
      
          public static Person getPerson(String name){
              if(person == null ){
                  person = new Person(name);
              }
              return person;
          }
      }
    • 이 덕분에 불변 클래스는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용 할 수 있다.
    • 플라이웨이트 패턴
    • 인스턴스 통제를 하여 클래스를 싱글턴, 인스턴스화 불가로 만들 수 있다.
    • 불변값 클래스에서 동치인 인스턴스가 단 하나임을 보장할 수 있다.
    • 열거타입(Enum)은 인스턴스가 하나만 만들어짐을 보장한다.
  3. 반환 타입의 하위타입 객체를 반환할 수 있다.
    • 반환할 객체의 클래스를 자유롭게 선택할 수 있는 '엄청난 유연성'을 선물한다.
    • 반환타입을 인터페이스로 주고 구현클래스를 API에 노출시키지 않고도 그 객체를 반환할 수 있어서 API를 작게 유지할 수 있다.
    • 클라이언트는 구현클래스를 모르지만 명시된 인터페이스대로 동작하는 객체를 얻을 수 있음을 알기 때문에 구현클래스 문서를 찾아서 알아야 할 필요가 없이 인터페이스로만 다룰 수 있다.
    • Collection collection = Collections.emptyList();
    • Collections 동반 구현클래스로 Collection 인터페이스에 구현클래스를 반환해 주어 사용자는 Collection 인터페이스만 사용하면 된다.
    • java 8부터는 인터페이스에 정적 메서드를 가질 수 없다는 제한이 풀려서 인스턴스화 동반 클래스를 둘 필요가 없어졌다.
  4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
    • 반환타입이 하위타입이기만 하면 어떤 클래스를 반환하더라도 상관없다.
    • EnumSet클래스 원소가 64개 이하면 long변수 하나로 관리하는 RegularEnumSet의 인스턴스를 65개 이상이면 long배열로 관리하는 JumboEnumSet의 인스턴스를 반환한다.
    • 클라이언트는 위의 두 클래스의 존재를 모르기 때문에 다음 릴리즈 때 성능을 업그레이드 하여 새로운 클래스를 추가해도 아무 상관이 없다.
  5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
    • 이런 유연함은 서비스 제공자 프레임워크(service provider framework)를 만드는 근간이 된다.
    • 서비스 제공자 프레임워크에서의 제공자(provider)는 서비스의 구현체다. 그리고 이 구현체들을 클라이언트에 제공하는 역할을 프레임 워크가 통제하여, 클라이언트를 구현체로 부터 분리해 준다.

서비스 제공자 프레임워크

핵심 컴포넌트

  • 서비스 인터페이스(service interface)
    • 구현체의 동작을 정의
    • Connection(JDBC)
  • 제공자 등록 API(provider registration API)
    • 제공자가 구현체를 등록할 때 사용
    • DriverManager.registerDriver(JDBC)
    • Driver d = new com.mysql.jdbc.Driver();
      DriverManager.registerDriver(d);
  • 서비스 접근 API(service access API)
    • 클라이언트가 서비스의 인스턴스를 얻을 때 사용
    • DriverManager.getConnection("url", "user", "password");
  • 서비스 제공자 인터페이스(service provider interface)
    • 서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체
    • 구현체를 인스턴스로 만들 때 리플랙션을 사용한다면 이 컴포넌트는 필요없다.
    • Driver (JDBC)가 서비스 제공자 인터페이스 역할을 한다.

 

단점

  1. 상속을 하려면 public or protected 생성자가 필요하기 때문에 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
  2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.

 

반응형