[Effective Java] 생성자 대신 정적 팩토리 메소드를 고려하라
ITEM 1. 생성자 대신 정적 팩토리 메소드를 고려하라
💡 클래스의 인스턴스 얻기
(1) PUBLIC 생성자
public class Computer {
private String os;
public Computer(String os) {
this.os = os;
}
}
public class Main {
public static void main(String[] args) {
Computer appleCom = new Computer("mac");
Computer windowCom = new Computer("window");
}
}
(2) 정적 팩토리 메소드
public class Computer {
private String os;
private Computer(String os) {
this.os = os;
}
public static Computer WindowComputer() {
return new Computer("window");
}
public static Computer AppleComputer() {
return new Computer("mac");
}
}
public class Main {
public static void main(String[] args) {
Computer.AppleComputer();
Computer.WindowComputer();
}
}
🔎 정적 팩토리 메소드 ?
📍 static method ?
- 클래스가 메모리에 올라갈 때 자동적으로 생성되어 인스턴스를 생성하지 않아도 호출이 가능하다.
🏭 Factory ?
= 객체를 생성하는 역할을 하는 클래스 정적 메서드
.
🔎 정적 팩토리 메소드의 장점
(1) 이름을 가질 수 있다.
// public 생성자
Computer(String, int, int)
// 정적 팩토리 메소드
Computer.appleComputer()
- public 생성자와 정적 팩토리 메소드중 어떤 것이 더 의미 설명이 잘 되어있는지 보면 정적 팩토리 메소드임을 알 수 있다. (애플컴퓨터? os는 mac이겠구나)
- public 생성자를 사용한다면 개발자는 매개변수의 순서와 어떤 역할을 하는지 잘 알고 있어야한다.
- 생성자는 메소드 시그니처의 제약이 있다.
(2) 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
public class Computer {
private static Map<String, Computer> computerMap = new HashMap<>();
private String OS;
public Computer(String OS) {
this.OS = OS;
}
static {
computerMap.put("APPLE", new Computer("MAC"));
computerMap.put("MICROSOFT", new Computer("WINDOW"));
computerMap.put("SAMSUNG", new Computer("TIZEN"));
}
public static Computer valueOf(String name) {
if(computerMap.containsKey(name)) {
return computerMap.get(name);
}
return new Computer(name);
}
}
- 불변 클래스는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다.
- 위의 Boolean class를 보면 상수 객체로 선언을 해줬기 때문에 매번 새로운 인스턴스를 생성하지 않는걸 볼 수 있다.
=> 생성비용이 큰 객체가 자주 요청되는 상황이라면 성능을 상당히 끌어올려준다. - 위의 Computer class를 보면 객체를 캐싱해서 쓰고있어 일일히 new 연산을 사용할 필요없이 valueOf 메서드를 활용하여 캐싱해둔 객체를 반환할 수 있다.
(2-1) 인스턴스 통제 클래스 : 인스턴스를 살아 있게 할지 철저히 통제할 수 있다.
- 인스턴스를 통제하면 클래스를 싱글톤으로 만들 수 있다. => #싱글톤 : 하나의 인스턴스만 생성해서 사용
class Singleton {
private Singleton(){};
private static final Singleton INSTANCE = new Singleton();
public Singleton getInstance() {
return INSTANCE;
}
}
- 인스턴스를 통제하면 인스턴스화 불가로 만들 수 있다. => #인스턴스화 : 클래스로부터 객체를 만든다. (new)
class Instantiae {
private Instantiae(){};
public Singleton getInstance() {
return new Instantiae();
}
}
- 불변 값 클래스에서 동치인 인스턴스가 단 하나뿐임을 보장할 수 있다. => 동치 : a.equals(b) = true;
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
- 열거 타입은 인스턴스가 하나만 만들어짐을 보장한다.=> 열거 타입은 상수이기 때문
public enum Enumeration {
FIRST, SECOND, THIRD ;
}
class Main {
public static void main(String[] args) {
Enumeration test1 = Enumeration.FIRST;
Enumeration test2 = Enumeration.SECOND;
Enumeration test3 = Enumeration.FIRST;
System.out.println(test1 == test2); // false
System.out.println(test1 == test3); // true
}
}
(3) 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
출처 : https://www.simplilearn.com/tutorials/java-tutorial/java-collectionpublic class Computer implements ComputerInterface{}
public interface ComputerInterface {
public static Computer getComputer() {
return new Computer();
}
}
- 반환할 객체의 클래스를 자유롭게 선택할 수 있게하는 엄청난 유연성
- API를 만들 때 유연성을 응용하면 구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있어 API를 작게 유지할 수 있다.
=> 인터페이스 기반 프레임워크를 만드는 핵심 기술 - Collections는 인스턴스화 불가 클래스이며 구현체들은 해당인터페이스에서 정적 팩토리 메서드를 통해 얻음
- Collection을 상속받는 모든 타입의 인스턴스가 리턴될 수 있다.
(4) 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
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의 noneOf()
-> universe.length <= 64 면 long변수 ReqularEnumSet 인스턴스 return
-> universe.length > 64 면 long 배열로 관리하는 JumboEnumSet 인스턴스 return - 반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.
- 필요 없는 하위 클래스는 삭제해도 문제가 없다.
(5) 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
- 서비스 인터페이스 : 구현체의 동작을 정의
- 제공자 등록 API : 제공자가 구현체를 등록할 때 사용
- 서비스 접근 API : 클라이언트가 서비스의 인스턴스를 얻을 때 사용
- 서비스 제공자 인터페이스 : 서비스 인터페이스의 인스턴스를 제공
- 클라이언트는 서비스 접근 API 를 사용할 때 원하는 구현체의 조건을 명시할 수 있다.
JDBC
<제공>
- 서비스 제공자 인터페이스 : Driver
- 제공자 등록 API : DriverManager.registerDriver()
=> 정적 팩토리 메서드
=> Driver가 로드되는 static에 호출, 제공자(DB)에 맞는 Connection 반환
<서비스>
- 서비스 인터페이스 : Connection
- 서비스 접근 API : Driver.Manager.getConnection()
=> Connection 인터페이스 return
=> 호출 되기 전, 미리 등록된 제공자가 있다고 확신하고 그에맞는 Connection return
🔎 정적 팩토리 메서드 단점
(1) 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다.
(2) 정적 팩토리 메서드는 프로그래머가 찾기 어렵다.
- 생성자처럼 API 설명에 명확히 드러나지 않음
Author And Source
이 문제에 관하여([Effective Java] 생성자 대신 정적 팩토리 메소드를 고려하라), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hye_b/Effective-Java-생성자-대신-정적-팩토리-메소드를-고려하라저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)