2장 객체 생성과 파괴 - 정적 팩터리 메서드

정지수 JisooJung·2021년 11월 20일
0

Effective Java 스터디

목록 보기
2/6

2장은 객체의 생성과 파괴를 다룬다.

아이템1. 생성자 대신 정적 팩터리 메서드를 고려하라

  • public 생성자: 클라이언트가 클래스의 인스턴스를 얻는 전통적 수단
  • 정적 팩터리 메서드(static factory method): 클래스의 인스턴스를 반환하는 단순 정적 메서드
// 정적 팩터리 메서드 예시)
public static Boolean valueOf(boolean b) {
	return b ? Boolean.TRUE : Boolean.FALSE;
}

정적 팩터리 메소드 장점

이름을 가짐

  • 생성자는 별도의 이름을 가지지 못함 -> 정적 팩터리 메서드는 이름을 통해 반환 객체의 특성을 잘 설명할 수 있음
  • 하나의 시그니처로 하나의 생성자만 만들 수 있음 -> 정적 팩터리 메서드는 이러한 제약 x
    시그니처가 같은 생성자가 여러개 필요할 시, 생성자를 정적 팩터리 메서드로 바꾸고 차이를 드러내는 이름 지어야 함
    참고) 시그니처(Signature): 메소드의 파라미터 순서, 타입, 개수
public class Car {
    
    public int num;

    // 생성자(이름 x)
    public Car(int num) {
        this.num = num;
    }

    // 정적 팩터리 메서드(이름 o: car)
    public static Car create(int num) {
        return new Car(num);
    }
}

호출 시 인스턴스 재사용 가능

  • 동일 객체 다수 요청 시 성능 UP
  • 인스턴스 통제: 반복되는 요청에 같은 객체를 반환하는 식으로 언제 어느 인스턴스를 살아 있게 할지 철저히 통제
    • 싱글턴(singleton) 클래스
    • 인스턴스화 불가(noninstantiable) 클래스
    • 불변 값 클래스에서 동치 인스턴스가 하나뿐임을 보장(a == b 일 때만 a.equals(b) 성립. feat. enum )
public class Car {
    
    private int num;
    private static Car[] arr = {new Car(0), new Car(1)};

    private Car(int num) {
        this.num = num;
    }

    // 정적 팩터리 메서드: arr 안에 선언된 객체 재사용
    public static Car of(int num) {
        if(num == 0) return arr[0];
        else if(num == 1) return arr[1];
        
        return null;
    }
}

반환 타입의 하위 타입 객체 반환 가능

  • 유연성 극대화
  • 구현 클래스를 공개하지 않고 그 객체 반환 가능 -> API를 작게 유지할 수 있게 됨
public class Car {
    
    private int num;

    public Car(int num) {
        this.num = num;
    }

    // 정적 팩토리 메소드
    public static Car createElectric(int num) {
        return new ElectricCar(num); // Car 클래스를 상속하는 ElectricCar 객체 리턴
    }
}

입력 매개변수에 따라 매번 다른 클래스의 객체 반환 가능

작성 시점에서 반환 객체의 클래스가 존재하지 않더라도 ok

  • 클라이언트를 구현체로부터 분리

  • 서비스 제공자 프레임워크의 근간 (ex. JDBC, 의존성 주입(DI) 프레임워크)

    • 3(+1)개의 핵심 컴포넌트로 이루어짐

      컴포넌트설명JDBC
      서비스 인터페이스
      (service interface)
      구현체의 동작 정의Connection
      제공자 등록 API
      (provider registration API)
      제공자가 구현체를 등록할 때 사용DriverManager.registerDriver
      서비스 접근 API
      (service access API)
      클라이언트가 인스턴스 접근 시 사용DriverManager.getConnection
      서비스 제공자 인터페이스
      (service provider interface)
      서비스 인터페이스의 인스턴스를 생성하는 팩터리 객체 (리플렉션으로 대체 가능)Driver

정적 팩터리 메소드 단점

상속 시 public이나 protected 생성자 필요

  • 정적 팩터리 메서드만 제공 시 하위클래스를 가질 수 없음
  • 그러나 컴포지션을 사용하도록 유도하며, 불변타입으로 만들 시 이 제약을 지켜야 하므로 장점으로 볼 수 있음

프로그래머가 찾기 어려움

  • API 설명에 명확히 드러나지 않으므로 문서화가 잘 되어있거나, 메서드 이름을 잘 지어야 함

정적 팩터리 메서드 네이밍 컨벤션

이름설명예시
from하나의 매개변수에 대한 인스턴스를 반환하는 형변환 메서드Date d = Date.from(instant);
of여러 매개변수를 받아 적당한 타입의 인스턴스를 반환하는 집계메서드Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
value Offrom과 of의 더 자세한 버전BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
instance / getInstance매개변수로 명시한 인스턴스 반환(같은 인스턴스임을 보장 x)StackWalker luke = StackWalker.getInstance(options);
create / newInstance매개변수에 대해 매번 새로운 인스턴스를 생성해 반환Object newArray = Array.newInstance(classObject, arrayLen);
getTypegetInstance와 동일하나, 다른 타입의 인스턴스를 반환할 때 사용FileStore fs = Files.getFileStore(path);
newTypenewInstance와 동일하나, 다른 타입의 인스턴스를 반환할 때 사용BufferedReader br = Files.newBufferedReader(path);
typegetType과 newType의 간결한 버전List<Complaint> litany = Collections.list(legacyLitany);

Reference

조슈아 블로크Joshua Bloch, 『이펙티브 자바 Effective Java 3/E』, 개앞맵시(이복연) 옮김, 인사이트(2018), p8-13.

  
profile
Study&Work&Log

0개의 댓글