Search
Duplicate

아이템 01 - 생성자 대신 정적 패터리 메서드를 고려하라

작성자
챕터
2장 - 객체 생성과 파괴
최종 편집
2023/09/13 12:39
생성 시각
2023/07/09 19:48

정적 팩터리 메소드 (static factory method)

요약

객체를 새로 생성해서 반환하거나,
기존에 있는 객체를 반환하도록 하는 메소드
메소드에 static 을 붙여서 사용하는 형태
생성자 처럼 사용할 수 있다.
객체를 생성하지 않고 함수를 호출 할 수 있다.

장점 & 설명

1. 생성자처럼 쓰면서 이름을 가질 수 있다.

이름을 가질 수 있다는 말은 객체의 특성을 설명할 수 있다는걸 뜻한다.

예시 BigInteger

//BigInteger Class public BigInteger(int numBits, Random rnd) { ... } public static BigInteger probablePrime(int bitLength, Random rnd) {...}
Java
복사
BigInteger() 생성자 와 BigInteger.probablePrime()
[생성자] BigInteger(int numBits, Random rnd)
함수에 대한 설명
BigInteger.probablePrime() 상세 구현 설명
한 클래스에 시그너처(전달인자) 가 같은 생성자가 여러개 필요할 경우에 사용 가능
BigInteger.probablePrime(int, Random) 는 랜덤 소수 생성
BigInteger(int, Random) 는 랜덤한 10진수 반환 생성자

2. 호출 때마다 인스턴스를 새로 생성하지 않아도 된다.

Boolean.valueOf()

@IntrinsicCandidate // JIT 컴파일러 내장 후보로 등록 -> 하드웨어 아키텍쳐 명령어로 실행한다는 뜻 -> 대충 최적화 public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } 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); ... 생략 }
Java
복사
Primitive Type → Wrapper class 로 반환해주는 클래스이다.
이때 TRUE 와 FALSE 는 시스템상 인스턴스가 하나만 존재하도록 위처럼 구현되어있다.
위와 같은 형태를 인스턴스 통제(instance-controlled) 클래스 라고도 한다.
싱글턴 테크트리 혹은 인스턴스화 불가 테크트리로 갈 수 있다.
인스턴스가 하나이기때문에, 객체비교인 a==b 와 a.equals(b) 가 성립된다.
JAVA 에서 일반적으로 equals 는 객체의 값 비교
== 는 두 개체의 참조 비교 (메모리 위치 비교)

3. 반환 타입의 하위 타입 객체를 반환할 수 있다

아래 예시 참조

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

public class EnumSetExample { enum Color { RED, BLUE, GREEN} public static void main(String[] args) { EnumSet<Color> smallSet = EnumSet.of(Color.RED, Color.BLUE); // RegularEnumSet 반환 //EnumSet<Color> largeSet = EnumSet.allOf(Color.class); // RegularEnumSet 반환 //65개이상 JumboEnumSet - LONG System.out.println(smallSet.getClass().getSimpleName()); // RegularEnumSet 출력 // System.out.println(largeSet.getClass().getSimpleName()); // RegularEnumSet 출력 } } public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) { EnumSet<E> result = noneOf(e1.getDeclaringClass()); result.add(e1); result.add(e2); return result; } /* public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) { EnumSet<E> result = noneOf(elementType); result.addAll(); return result; } */ 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); }
Java
복사
EnumSet
EnumSet.of → nonOf
EnumSet 클래스는 Enum 이 정의된게 64개 이상일경우 JumboEnumSet ( long type) 으로 생성
64개 미만일경우 RegularEnumSet (int type) 으로 생성된다.
위와 같이 세부 구현사항을 몰라도 우리는 그냥 EnumSet 을 만들어주는대로 사용할 수 있다.
만약 세부구현이 바뀐다고해도, 클라이언트 코드는 그대로 EnumSet.of 로 사용할 수 있다.

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

단점

1. 상속을 하려면 public이나 protected 생성자 필요 따라서 정적 팩터리 메서드만 있다면 상속 / 구현 불가능

2. 찾기 어려움 - 규약 지켜야함

From : 매개변수 받아서 형변환 인스턴스 반환
of : 매개변수 받아서 인스턴스 반환
valueOf: from + of
getInstance, instance : 매개변수로 명시한 인스턴스 반환 같은 인스턴스임을 보장하지 않음
create, newInstance : 매번 새로운 인스턴스를 생성
getType : 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할때
FileStore fs = Files.getFileStore(path)
path 전달인자로 abstract FileStore 객체리턴 하지만 Files 라는 관리 객체에서 리턴 ⇒ 더큰 개념에서 하위 개념이지만 포함은 아닐때 사용하는듯
public static FileStore getFileStore(Path path) throws IOException { return provider(path).getFileStore(path); } // public abstract class FileStore { /** * Initializes a new instance of this class. */ protected FileStore() { }
Java
복사
newType : 다른클래스에서 새로운 인스턴스 반환
type : getType과 newType의 간결한 버전
List<Complaint> litany = Collections.list(legacyLitany);
public class Collections { // Suppresses default constructor, ensuring non-instantiability. private Collections() { } ...생략 public static <T> ArrayList<T> list(Enumeration<T> e) { ArrayList<T> l = new ArrayList<>(); while (e.hasMoreElements()) l.add(e.nextElement()); return l; }
Java
복사

정적 팩토리

정적 팩토리 객체 재사용(캐시) 예시코드

import java.util.HashMap; import java.util.Map; public class Effective { class User { private String name; public User(String name) { this.name = name; } @Override public boolean equals(Object obj) { if (obj instanceof User) { return ((User) obj).name.equals(this.name); } return false; } } class UserFactory { private static Map<String, User> userMap = new HashMap<>(); public static User getUser(String name) { User user = userMap.get(name); if (user == null) { user = new Effective().new User(name); userMap.put(name, user); } return user; } } public static void main(String[] args) { User user1 = new Effective().new User("user"); User user2 = new Effective().new User("user"); System.out.println("user1.equals(user2) = " + user1.equals(user2)); System.out.println("user1 == user2 = " + (user1 == user2)); System.out.println("===================================================="); User user3 = UserFactory.getUser("user"); User user4 = UserFactory.getUser("user"); System.out.println("user3.equals(user4) = " + user3.equals(user4)); System.out.println("(user3 == user4) = " + (user3 == user4)); } }
Java
복사

하위타입 반환 예제

/** INTERFACE */ public interface Seoul42Human { void evaluate(Seoul42Human target); String getName(); } /** CADET **/ public class Cadet implements Seoul42Human { private String name; private int remainBlackhole; private Cadet(String name, int remainBlackhole) { this.name = name; this.remainBlackhole = remainBlackhole; } public static Cadet create(String name, int remainBlackhole) { return new Cadet(name, remainBlackhole); } @Override public void evaluate(Seoul42Human target) { System.out.println("target " + target.getName() + " evaluated by Cadet " + this.name); } @Override public String getName() { return this.name; } } /** MEMBER **/ public class Member implements Seoul42Human{ private String name; private Member(String name) { this.name = name; } public static Member create(String name) { return new Member(name); } @Override public String getName() { return this.name; } @Override public void evaluate(Seoul42Human target) { System.out.println("target " + target.getName() + " evaluated by Member " + this.name); } } /** MAIN */ public class GaepoCluster { public static void main(String[] args) { Seoul42Human cadet1 = Cadet.create("daewoole", 123); Seoul42Human member1 = Member.create("jpark2"); member1.evaluate(cadet1); cadet1.evaluate(member1); } } //target daewoole evaluated by Member jpark2 //target jpark2 evaluated by Cadet daewoole
Java
복사