반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 디자인패턴
  • 메이븐 라이프사이클
  • 자바스크립트 면접
  • 싱글턴
  • prerendering
  • next js app
  • try-with-resources
  • Java
  • 서버리스 컴퓨팅
  • 자바 디자인패턴
  • 이펙티브 자바 item9
  • 프로그래머의 뇌
  • NEXT JS
  • 스프링 검증
  • Next.js
  • 토비의 스프링
  • rust
  • 플라이웨이트패턴
  • 참조 해제
  • bean Validation
  • pnpm
  • 외래키 제약조건
  • 알고리즘
  • item8
  • 자바스크립트
  • 자료구조
  • MSSQL
  • item7
  • 메이븐 페이즈
  • 추상 팩터리 패턴
  • 자바
  • 메이븐 골
  • 슬로우 쿼리
  • 자바 검증
  • 객체복사
  • Javadoc 자바독 자바주석 주석 Comment
  • item9
  • serverless computing
  • 이펙티브자바
  • 러스트
  • Pre-rendering
  • 팩터리 메서드 패턴
  • 빈 검증
  • 작업기억공간
  • 싱글톤 패턴
  • 이펙티브 자바
  • 자바스크립트 인터뷰
  • 느린 쿼리
  • 도커공식문서
  • 싱글톤

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

Java/이펙티브 자바

이펙티브 자바, 쉽게 정리하기 - item1. 생성자 대신 정적 팩터리 메서드를 고려하라

2021. 12. 22. 22:02

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

핵심 요약

  • 객체 생성에는 보통 생성자만 있다고 생각하기 쉽다.
  • 정적 팩토리 메서드를 객체 생성 용도로 쓰는 것도 경우에 따라 좋다.

생성자 대신 정적 팩터리 메서드를 만들면 가지는 장점들

장점1: 생성자가 이름을 가질 수 있다.

public class Test {
    @org.junit.jupiter.api.Test
    public void bigInteger() {
        BigInteger bigInteger = new BigInteger(10, 100, new Random());
        BigInteger probablePrime = BigInteger.probablePrime(10, new Random());
        System.out.println("bigInteger = " + bigInteger);
        System.out.println("probablePrime = " + probablePrime);
    }
}
  • 위 예에서 2가지 방식으로 소수 형태의 BigInteger를 생성할 수 있다.
    • 생성자는 단순히 봐서 의도를 알기 어렵다. 아마 랜덤한 숫자를 생성하는 것 같다.
    • probablePrime이라는 정적 팩토리 메서드를 통한 객체 생성은 한눈에 봐도 의도가 명확하다.

물론 문서를 찾아봐도 되지만, 그 시간을 줄이고 더욱 명확한 이해와 함께 코드를 작성할 수 있다.

장점2: 매 호출 시 인스턴스를 새로 생성할 필요가 없다.

@jdk.internal.ValueBased
public final class Boolean implements java.io.Serializable,
                                      Comparable<Boolean>, Constable
{
    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);
    ...
}
  • Boolean 클래스 같은 경우 상태가 한정적이다.
    • 멤버 필드인 value에 들어올 수 있는 값은 true 혹은 false이다.
    • 매번 인스턴스를 새로 만들 필요 없이 .valueOf() 메서드를 이용하면, static한 객체를 반환해준다.
  • 플라이웨이트 패턴 참조링크에서 플라이웨이트 패턴의 예제를 볼 수 있다.
    • 용어가 생소할 수는 있어도 그냥 static한 저장소에 객체를 저장해놓고 불러다 쓰는 것일 뿐이다.
    • 항상 같은 객체를 반환하는 팩토리 메서드를 정적 팩토리 메서드라 하는데, 이런 방식을 인스턴스 통제(instance-controlled)라고 한다.
      • 인스턴스 통제는 싱글톤, 인스턴스화 불가, 1개의 동치 보장 (equals와 ==이 같은 결과 반환)에 다양하게 사용된다.
      • 열거(enum) 타입은 인스턴스가 하나임이 보장되는 타입이다.

장점3: 반환 타입의 하위 타입을 반환하는 것도 가능하다.

  • 이를테면 인터페이스인 List를 반환하는 메서드를 만들고, 매개변수에 따라 ArrayList, LinkedList 등을 반환하는 것이 가능한 것이다.
    • 기본 클래스 생성자를 사용하면 인터페이스 반환이 불가능하다.
  • 클라이언트의 입장에서는 오직 인터페이스 만을 가지고 코드를 작성할 수 있게 되어 매우 유연해진다.
    • 인터페이스 기반으로 코딩하는 것은 일반적으로 좋은 습관이다.
  • 자바 컬렉션 프레임워크는 내부적으로 45개의 유틸리티 구현체를 제공하는데, 이 구현체 대부분을 단 하나의 인스턴스화 불가 클래스인 java.util.Collections의 정적 팩토리 메서드에서 얻게 했다.
  • 자바 8부터는 인터페이스에 public 정적 메서드 작성을 지원하기 때문에 인터페이스를 반환하는 여러가지 구현체를 만들어둘 수 있다.

장점4: 인자에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

  • 인터페이스를 반환하면, 하위 타입 어떤 객체를 반환하든 상관 없기 때문이다. (장점3과 일맥상통하는 부분이 있다.)
  • List를 반환하는 정적 메서드가 있다면, 인자에 따라 ArrayList와 LinkedList를 반환하는 것이 가능하다.
    • 매개변수를 하나 받아서 대량의 데이터 삽입 삭제가 일어난다면 LinkedList를 받고, 아니라면 ArrayList를 받을 수 있다.
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
    Enum<?>[] universe = getUniverse(elementType);
    if (universe == null)
        throw new ClassCastException(elementType + " not an enum");

    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);
}

책에서 나온 EnumSet의 예제이다. EnumSet이란, 그냥 Set인데, Enum타입에 특화된 것이다. 위의 EnumSet은 데이터 64개를 기준으로 RegularEnumSet과 JumboEnumSet 중 어느것을 반환할지 결정한다.

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

  • 이 부분도 사실 장점3 과 장점4의 연장선이다.
  • JDBC와 같은 서비스 제공자 프레임워크에서 이러한 기능을 적극 활용한다.
    • JDBC는 대표적인 서비스 제공자 프레임워크이다.
    • 서비스 제공자 프레임워크는 서비스 인터페이스, 제공자 등록 API, 서비스 접근 API로 이루어진다.
      • 간혹 서비스 제공자 인터페이스가 쓰이기도 하며, 서비스 제공자 인터페이스가 없다면, 리플렉션을 사용한다.
      • JDBC 에서는
        • 서비스 인터페이스: Connection
        • 제공자 등록 API: DriverManager.registerDriver
        • 서비스 접근 API: DriverManager.getConnection
        • 서비스 제공자 인터페이스: Driver
  • 서비스 접근 API에게 클라이언트는 원하는 구현체의 조건을 명시한다.
    • JDBC에서는 어떤 DB를 사용할 것인지, DB 접속 경로, 아이디, 패스워드 등을 입력한다.
    • 조건을 명시하지 않으면, 기본 구현체를 하나씩 돌아가며 반환한다.
    • 이 서비스 접근 API가 사실 '유연한 정적 팩토리'의 실체다.

서비스 제공자 인터페이스가 사용되는 경우, 인스턴스를 생성하는 팩터리 객체를 설명해준다.

  • 인터페이스에 직접 객체를 주입하거나 주입받는 브리지 패턴 혹은 의존 객체 주입 프레임워크도 강력한 서비스 제공자로 볼 수 있다.

단점들

단점1: 상속에는 public 혹은 protected 생성자가 필요하므로 정적 팩토리 메서드만 제공할 경우, 상속할 수 없다.

  • 상속보다는 컴포지션을 사용하는게 낫고(item 18), 불변 타입으로 만드는 것이 좋다는 점(item 17)을 생각하면, 오히려 장점일 수 있다.

단점2: 정적 팩토리 메서드를 다른 개발자가 찾기 어렵다.

  • 생성자처럼 API에 명확히 드러나지 않기 때문이다.
  • API 문서를 잘 써놓고, 메서드 이름도 규약에 따라 짓는 등 노력할 필요가 있다.

정적 팩토리 메서드 명명방식

  • from: 매개변수를 하나 받아서 해당 타입의 인스턴스 반환하는 형변환 메서드
    • Date d = Date.from(instant)
  • of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
    • Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING): EnumSet 반환
enum Cards {
    ACE, JACK, QUEEN, KING
}

@Test
public void enumSetTest() {
    Set<Cards> cards = EnumSet.of(ACE, JACK);

    for (Cards card : cards) {
        System.out.println("card = " + card);
    }
}
  • valueOf: from과 of의 더 자세한 버전
    • BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance, getInstance: 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하진 않는다.
    • StackWalker luke = StackWalker.getInstance(options)
  • create, newInstance: getInstance와 비슷하지만, 매번 새로운 객체를 반환한다.
    • Object newArray = Array.newInstance(classObject, arrayLen)
  • getType: getInstance와 같으나 생성할 클래스가 아닌 다른 클래스의 팩터리 메서드를 정의할 때 쓴다. Type 이 팩터리 메서드가 반환할 타입이 된다.
    • FileStore fs = Files.getFileStore(path)
  • newType: newInstance와 같으나 마찬가지로 생성할 클래스가 아닌 다른 클래스의 팩터리 메서드를 정의할 때 쓴다. 마찬가지로 _Type_이 반환할 타입이다.
  • type: getType과 newType의 간단한 버전이다.
    • List<Complaint> litany = Collections.list(legacyLitany)

정리

  • 무작정 public으로 생성자를 만들기 전에 정적 팩토리 메서드가 더 좋진 않을까 생각해보자.
    • 특이한 종류의 생성자라면? -> 정적 팩토리 메서드 패턴으로 이름을 지정하는게 더 좋을 수 있다.
    • 싱글톤 혹은 플라이웨이트 패턴을 사용하는게 효율적이라면?
    • 유연하게 인터페이스를 반환하고 싶다면?
    • 인자에 따라 다른 타입을 반환하고 싶다면?
반응형

'Java > 이펙티브 자바' 카테고리의 다른 글

이펙티브 자바, 쉽게 정리하기 - item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라  (0) 2021.12.25
이펙티브 자바, 쉽게 정리하기 - item4. 인스턴스화를 막으려면 private 생성자를 사용하라  (0) 2021.12.25
이펙티브 자바, 쉽게 정리하기 - item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라  (0) 2021.12.24
이펙티브 자바, 쉽게 정리하기 - item2. 생성자에 매개변수가 많다면, 빌더 패턴을 고려하라  (0) 2021.12.23
이펙티브 자바 - 들어가면서...  (0) 2021.12.22
    'Java/이펙티브 자바' 카테고리의 다른 글
    • 이펙티브 자바, 쉽게 정리하기 - item4. 인스턴스화를 막으려면 private 생성자를 사용하라
    • 이펙티브 자바, 쉽게 정리하기 - item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라
    • 이펙티브 자바, 쉽게 정리하기 - item2. 생성자에 매개변수가 많다면, 빌더 패턴을 고려하라
    • 이펙티브 자바 - 들어가면서...
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바