자바 디자인 패턴 용어 사전
생성 패턴static factory · valueOf · of()

정적 팩토리 메서드

이름을 가진 생성자. GoF Factory Method와는 다른 것이며 캐싱·하위타입 반환이 가능하다.

이름을 가진 생성자 역할을 하는 정적 메서드(Effective Java Item 1).

List.of(1, 2, 3)
Optional.ofNullable(x)
Duration.ofSeconds(3)
Integer.valueOf(100)

GoF Factory Method와 무엇이 다른가

  • GoF Factory Method — 하위 클래스가 '무엇을 만들지' 결정한다

    • 상속이 핵심 장치다
    • 추상 메서드 createXxx() 를 하위가 구현한다
  • 정적 팩토리 — 이름 있는 생성자일 뿐이다

    • 상속과 무관하고, 패턴이라기보다 관용구다

실무 대화에서는 뭉뚱그려 '팩토리' 라 부르지만 면접에서는 이 구분을 묻는 경우가 있다

생성자에 없는 능력

  • ① 이름을 가진다

    • BigInteger.probablePrime(bits, rnd)
    • new BigInteger(...) 로는 무엇을 만드는지 알 수 없다
  • ② 매번 새 인스턴스를 만들지 않아도 된다

    • Integer.valueOf(100) → 캐시된 인스턴스 반환 (-128~127)
    • Boolean.valueOf(true) → 상수 반환
    • new 는 반드시 새 객체를 만든다 — 선택권이 없다
  • ③ 하위 타입을 반환할 수 있다

    • List.of() → 원소 수에 따라 다른 내부 구현체를 준다
    • Collections.unmodifiableList(x) → 구현 클래스를 감춘다
    • 반환 타입을 인터페이스로 두면 구현을 자유롭게 바꿀 수 있다
  • ④ 인자에 따라 다른 클래스를 줄 수 있다

    • EnumSet.of(...) → 원소 64개 이하면 RegularEnumSet, 넘으면 JumboEnumSet
    • 호출자는 이 사실을 몰라도 된다

단점

  • public·protected 생성자가 없으면 상속할 수 없다
    • (합성을 유도한다는 점에서 장점으로 보기도 한다)
  • 생성자처럼 눈에 띄지 않아 문서를 봐야 찾을 수 있다
    • 관례적인 이름을 쓴다: of · valueOf · from · getInstance · newInstance

실무에서의 판단

값 객체·불변 객체를 만들 때는 생성자를 private 으로 막고 정적 팩토리만 노출하는 편이 유리하다

  • 검증을 한곳에 모을 수 있다
  • 나중에 캐싱을 넣어도 호출부가 바뀌지 않는다

record 는 컴팩트 생성자로 검증하고, 필요하면 정적 팩토리를 함께 둔다

관례적인 이름과 그 의미

of / valueOf     인자를 그대로 담는다        List.of · Integer.valueOf
from             다른 타입에서 변환한다      Date.from(instant)
getInstance      인스턴스를 준다 (같을 수도) Calendar.getInstance()
newInstance      매번 새로 만든다            Array.newInstance()
create           새로 만든다                 Files.createFile()
type / getType   다른 클래스의 인스턴스를 준다 Collections.list(e)
  • 이름 자체가 계약이다 — getInstance 가 매번 새 객체를 준다면 오해를 부른다

캐싱이 만드는 실제 현상

Integer a = Integer.valueOf(127), b = Integer.valueOf(127);
a == b;   // true  — 캐시된 같은 인스턴스

Integer c = Integer.valueOf(128), d = Integer.valueOf(128);
c == d;   // false — 128부터는 새 객체

기본 캐시 범위 -128 ~ 127 상한은 -XX:AutoBoxCacheMax=N 으로 조정할 수 있다

'작은 값에서는 멀쩡하다 큰 값에서만 틀어지는' 버그의 원인이다 캐시는 성능용이지 동일성 보장이 아니다 → 값 비교는 언제나 equals

면접 함정

  • "정적 팩토리 = 팩토리 메서드 패턴" → 상속 기반의 GoF 패턴과 다르다.
  • "getInstance면 싱글톤" → 이름 관례일 뿐, 매번 새 객체를 줄 수도 있다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 실무 적용 심화 — 프레임워크 소스로 읽는 패턴