1. 정적 팩토리 메서드란?
정적 팩토리 메서드는 인스턴스를 생성자로 생성하지 않고, 별도의 객체 생성의 역할을 하는 정적 메소드를 사용해 인스턴스를 생성하는 방식입니다.
ex)자바의 LocalTime 클래스내 정적 팩토리 메서드
public static LocalTime of(int hour, int minute) {
HOUR_OF_DAY.checkValidValue(hour);
if (minute == 0) {
return HOURS[hour];
}
MINUTE_OF_HOUR.checkValidValue(minute);
return new LocalTime(hour, minute, 0, 0);
}
위 예시 코드에서 본 LocalTime의 of 메서드처럼 생성자를 사용하지 않고 정적 메서드를 사용해 객체를 생성하는 것을 정적 팩토리 메서드 라고 합니다.
그렇다면 객체 생성하는 역할을 제공하는 생성자가 있는데 왜 정적 팩토리 메서드를 따로 만들어 사용하는지 지금부터 알아보도록 하겠습니다.
2. 정적 팩토리 메서드의 장점
이펙티브 자바에서 소개된 아이템 중 하나가 '생성자 대신 정적 팩토리 메서드를 고려하라' 입니다.
정적 팩토리 메서드를 사용함으로써 오는 장점에 대해 지금부터 알아보도록 하겠습니다.
2-1 이름을 가질 수 있다.
생성자에 넘기는 매개변수와 생성자 자체로는 반환될 객체의 특성을 알기 어렵습니다. 이 때 프로그래머는 객체 생성시 내부구조를 알고 있어야 목적에 맞게 객체를 생성할 수 있습니다.
하지만 정적 팩토리 메서드를 사용하면 이름을 통해 반환될 객체의 특성을 쉽게 묘사할 수 있습니다.
public class Car {
private String name;
private String type;
private Car(String name, String type) {
this.name = name;
this.type = type;
}
public static Car createElectricCar(String name) {
return new Car(name, "ElectricCar");
}
public static Car createOilCar(String name) {
return new Car(name, "OilCar");
}
}
Car electricCar = Car.createElectricCar("테슬라");
Car oilCar = Car.createOilCar("쏘나타");
createElectricCar, createOilCar 두 정적 팩토리 메서드를 통해 이름만 보아도 전기차를 생성하는지 경유차를 생성하는지 단번에 이해할 수 있습니다. 이처럼 정적 팩토리 메서드를 사용하면 객체 생성의 목적을 이름에 표현할 수 있어 가독성과 생산성을 높여줄 수 있습니다.
2-2 호출할 때마다 새로운 인스턴스를 새로 생성하지 않아도 된다.
ex1) 싱글톤에서의 활용
public class Car {
private static final Car instance = new Car();
private Car() {}
public static Car getInstance() {
return instance;
}
}
생성자를 외부에서 생성할 수 없도록 private로 막아놓고 getInstance()라는 정적 팩토리 메서드를 통해 새로운 인스턴스를 생성하지 않아도 객체를 재사용해 메모리를 아낄 수 있습니다.
ex2) 캐싱(Caching)
public class Car {
private final String name;
private final String type;
// private 생성자
private Car(String name, String type) {
this.name = name;
this.type = type;
}
// 저장된 인스턴스를 관리하기 위한 맵
private static final Map<String, Car> carCache = new HashMap<>();
// 전기차 생성 메서드 (기존 인스턴스 반환)
public static Car createElectricCar(String name) {
if (!carCache.containsKey(name)) {
carCache.put(name, new Car(name, "Electric");
}
return carCache.get(name);
}
// 경유차 생성 메서드 (기존 인스턴스 반환)
public static Car createOilCar(String name) {
if (!carCache.containsKey(name)) {
carCache.put(name, new Car(name, "Oil"));
}
return carCache.get(name);
}
}
위 코드에서 정적 팩토리 메서드가 호출되면 해당 인스턴스가 존재하는지 확인하고 존재하면 그대로 반환해 재사용하고, 없다면 새로 생성해 map에 추가해주게 됩니다.
이처럼 자주 재사용되는 인스턴스를 캐싱해 재활용하는 방식을 통해 불필요한 객체 생성을 막아 메모리를 절약할 수 있습니다.
2-3 하위 자료형 객체를 반환할 수 있다.
만약 Car라는 상위 타입을 상속 받는 ElectricCar, OilCar가 있다고 생각해봅시다. 타입에 따른 하위 등급 타입을 반환하는 정적 팩토리 메소드를 만들면 분기 처리를 통해 하위 타입 객체를 반환할 수 있습니다.
public static Car createCar(String type) {
if(type.equals("Electric")) {
return new ElectricCar();
}
if(type.equals("Oil")) {
return new OilCar();
}
}
2-4 객체 생성을 캡슐화 할 수 있다.
정적 팩토리 메서드를 통해 객체 생성을 캡슐화할 수도 있습니다.
캡슐화란?
데이터 은닉 객체의 상테를 외부에서 직접 접근하지 못하도록 막고 객체의 동작을 통해서 상태를 변경하거나 접근할 수 없도록 하는 객체 지향 프로그래밍 원칙
public static Car createCar(String type) {
if(type.equals("Electric")) {
return new ElectricCar();
}
if(type.equals("Oil")) {
return new OilCar();
}
}
위 예시에서 클라이언트는 팩토리 메서드를 호출하기만 하면되고 어떤 클래스의 객체가 생성되는지 알 필요가 없습니다.
또한 새로운 타입의 자동차가 필요한다면 팩토리 메서드만 수정하면 됩니다 이를 통해서 유연한 확장성을 가질 수 있고
내부 구현을 드러내지 않아 캡슐화할 수 있다는 장점이 있습니다.
3. 정적 팩토리 메서드 의 단점
1. 상속을 하려면 public이나 protected 생성자가 필요하게 됩니다. 하지만 정적 팩토리 메서드만 제공하게 되면 생성자로 인스턴스를 생성하는 것을 막기 위해 생성자 접근제어자를 private으로 설정하게 되어 상속이 불가능하게 됩니다.
2. 정적 팩토리 메서드는 프로그래머가 찾기 어렵습니다. 다른 개발자들이 사용할 경우 정적 팩터리 메서드 방식 클래스를 인스턴스화 할 방법을 알아내야합니다.
이런 문제점을 해결하기 위해 흔히 사용되는 명명 방식들을 이용하거나 API 문서를 제공하는 방법 있습니다.
4. 자주 사용되는 정적 팩토리 메서드 명명 방식
- from : 매개변수를 하나 받아 해당 타입 인스턴스를 반환
- of : 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환
- valueOf : from과 of의 더 자세한 버전
- instance, getInstance : 매개변수로 명시한 인스턴스를 반환(같은 인스턴스임을 보장하지는 않음)
- create, newInstance : 매번 새로운 인스턴스를 반환함을 보장
참고
이펙티브 자바
'JAVA' 카테고리의 다른 글
[JAVA] 불변객체란? (0) | 2024.06.09 |
---|---|
[JAVA] 가비지 컬렉션 2편 (알고리즘) (0) | 2024.06.04 |
[JAVA] 가비지 컬렉션이란? (Garbage Collection) 1편 (0) | 2024.06.04 |
[JAVA] Call by Value, Call by Reference (0) | 2024.06.03 |
[JAVA] 래퍼 클래스(Wrapper Class)란? (0) | 2024.05.31 |