Effective Java 02 - item 01 -

생성자 대신 정적 팩터리 메서드를 고려하라

static 메소드를 사용하여 생성자를 대체할 수 있다.

생성자 대신 정적 메소들르 사용하는 것에 어떤 이점이 있을까?

첫 번째, 이름을 가질 수 있다.

소수를 반환하는 메소드를 생각해볼 때,

BigInteger(int, int, Random)

VS

BigInteger.probablePrime()

무조건 아래쪽이 더 의미가 가깝다고 느껴진다.

이는 클린코드에서 나온 내용과도 일맥상통하는 내용이다.

생성자 대신 정적 메소드를 통해 하고자하는 행위가 이름을 가진다는 것은 읽는 사람으로 하여금 결국 더욱 더 코드를 풍부하게 만들어준다.

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

Boolean.valueOf(boolean)

위 코드는 내부적으로 객체를 아예 생성하지않는다.

인스턴스를 캐싱해놓고 재활용하기 때문에 불필요한 객체 생성을 줄일 수 있다.

플라이웨이트 패턴(FlyWeight Pattern)이라는 디자인 패턴도 이와 비슷한 기법이라고 한다.

세 번째, 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.

생성자는 반드시 생성하려는 생성자에 해당하는 타입이 반환되지만, 정적 메소드를 사용하면 하위 타입의 객체도 반환이 가능하다.

자바 8이전에는 인터페이스에 정적 메서드를 선언할 수 없어서, 인터페이스 내부에 Companion Class를 두는 것이 관례였다.

자바 8 이후부터 Default Method가 등장하면서 Companion Class를 둘 이유가 사라졌다.

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

EnumSet 클래스는 생성자 없이 오직 정적 팩터리 메서드만 제공한다.

엘리먼트가 64개 이하면 RegualrEnumSet을, 65개 이상이면 JumboEnumSet을 반환해준다.

클라이언트 입장에서는 팩터리가 건네주는 객체가 클래스의 인스턴스인지를 모르고, 알 필요도 없다.

만약 RegualrEnumSet을 굳이 쓸 필요가 없다면 지워버리면 그만이다.

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

이러한 유연함은 서비스 제공자 프레임워크를 만드는데 근간이 되는데, 대표적으로 JDBC가 있다.

구현체들을 클라이언트에 제공하는 역할을 프레임워크가 통제하여, 클라이언트는 구현체를 모르더라도 해당 기능을 사용할 수 있게 해준다.

서비스 제공자 프레임워크는 3개의 핵심 컴포넌트로 이루어진다.

Service Interface, *Provider Registration API, Service Access API

클라이언트는 Service Access API를 사용할 때 원하는 구현체의 조건을 명시해줄 수 있다.

만약 조건이 없다면 기본 구현체를 반환하거나 지원하는 구현체들을 하나씩 돌아가며 반환해준다.

Service Access API가 바로 서비스 제공자 프레임워크의 근간인 유연한 정적 팩터리이다.

정적 메서드 팩토리의 단점

첫 번째 상속을 하려면 public, protected 생성자가 필요하니, 정적 팩터리 메소드만 제공하면 하위 클래스를 만들어낼 수 없다.

물론 이러한 패턴이 상속을 지양하고 컴포지션을 유도하다보니, 이게 어떻게 보면 장점이 될 수도 있다.

두 번째, 정적 팩터리 메서드는 프로그래머가 찾기가 어렵다.

생성자처럼 API 설명에 명확히 드러나지가 않아서 문서를 읽어봐야한다.

그러나 요즘은 IDE가 좋아서 딱히..?

정적 팩토리 메서드의 예시는 다음과 같다.

from: 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드.

of: from과 다르게 여러 매개변수를 받는다.

valueOf: fromof보다 더 자세한 버전

instance : 혹은 getInstance 매개변수로 명시한 인스턴스를 반환하지만 같은 인스터임을 보장해주진 않는다.

create: 혹은 newInstance 항상 새로운 인스턴스를 생성해 반환해준다.

getType: 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 생성할 때 사용한다. 여기서 Type은 해당 팩터리 메서드가 반환할 객체의 타입을 적어준다. e.q. ) getMember(name)

newType: 다른 클래스에 새로운 인스턴스를 생성하여 반환할 때 사용한다.

type: getTypenewType의 간결 버전

Reference

스크린샷 2021-04-16 오후 4 24 23

이펙티브 자바 Effective Java 3/E

조슈아 블로크



© 2022. by minkuk

Powered by minkuk