👨🏼💻 팩토리 메서드 패턴이란 객체 생성을 공장 클래스로 캡슐화 처리하여
대신 생성하게 하는 생성 디자인 패턴이다.
클라이언트에서 new
를 통해 제품 객체를 생성하는 것이 아닌, 제품 객체들을
도맡아 생성하는 공장 클래스를 만들고, 이를 상속하는 서브 공장 클래스의 메서드에서
여러 제품 객체 생성을 각각 책임지는 형태이다.
또한 객체 생성에 필요한 과정을 템플릿처럼 미리 구성해놓고, 객체 생성에 대한
전처리나 후처리를 통해 생성 과정을 다양하게 처리하여 객체를 유연하게 정할 수
있는 특징도 있다.
객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 하위클래스가 담당
패턴 사용 시기
패턴 장점
패턴 단점
식당에서는 사과를 디저트로 제공한다.
Restaurant
public class Restaurant {
public Apple servingApple() {
Apple apple = new Busa();
apple.wash();
apple.peel();
apple.slice();
return apple;
}
}
Apple
public abstract class Apple {
public abstract void wash();
public abstract void peel();
public abstract void slice();
}
Busa
public class Busa extends Apple {
@Override
public void wash() {
System.out.println("Busa : wash");
}
@Override
public void peel() {
System.out.println("Busa : peel");
}
@Override
public void slice() {
System.out.println("Busa : slice");
}
}
Hongok
public class Hongok extends Apple {
@Override
public void wash() {
System.out.println("Hongok : wash");
}
@Override
public void peel() {
System.out.println("Hongok : peel");
}
@Override
public void slice() {
System.out.println("Hongok : slice");
}
}
집에서는 사과를 아침으로 먹는다.
Home
public class Home {
public Apple getAppleForBreakFast() {
Apple apple = new Hongok();
apple.wash();
return apple;
}
}
사과의 종류가 변경되거나 추가될 경우, Restaurant
와 Home
의 코드가 변경되어야 한다.
사과의 인스턴스를 생성하는 모든 코드에서 아래와 같은 작업을 반복할 필요가 있다.
public class Restaurant {
public Apple servingApple(String kind) {
Apple apple = null;
if (kind.equals("busa")) {
apple = new Busa();
} else if (kind.equals("hongok")) {
apple = new Hongok();
}
apple.wash();
apple.peel();
apple.slice();
return apple;
}
}
이렇듯 변경이 자주 일어나는 부분은 분리하여 클래스로 캡슐화할 필요가 있다.
AppleFactory
public class AppleFactory {
public static Apple getApple(String kind) {
Apple apple = null;
if (kind.equals("busa")) {
apple = new Busa();
} else if (kind.equals("hongok")) {
apple = new Hongok();
} else if (kind.equals("hongro")) {
apple = new Hongro();
}
return apple;
}
}
Restaurant
public class Restaurant {
public Apple servingApple(String kind) {
Apple apple = AppleFactory.getApple(kind);
apple.wash();
apple.peel();
apple.slice();
return apple;
}
}
위와 같이 팩토리 메서드 패턴을 활용하면 새로운 사과 종류가 추가되어도,
팩토리 클래스만 변경하면 되고 실제 사과를 사용하는 클라이언트는 영향을 받지 않는다.
AppleFactory
public interface AppleFactory {
Apple getApple();
}
BusaFactory
public class BusaFactory implements AppleFactory {
@Override
public Apple getApple() {
return new Busa();
}
}
HongokFactory
public class HongokFactory implements AppleFactory {
@Override
public Apple getApple() {
return new Hongok();
}
}
팩토리 객체들은 여러 개가 존재할 필요가 없다. 따라서 Lazy-Holder 패턴과 같은
싱글톤 패턴을 이용하여 구현하는 것이 메모리를 최적화시킨다.
public class BusaFactory implements AppleFactory {
private BusaFactory() {
}
public static class InstanceHolder {
private static final BusaFactory INSTANCE = new BusaFactory();
}
public static BusaFactory getInstance() {
return InstanceHolder.INSTANCE;
}
@Override
public Apple getApple() {
return new Busa();
}
}
Java 8버전 이후 추가된 default method
와 Java 9이후에 추가된 private
메서드를 통해 인터페이스로 추상 클래스를 통해 구현되어야 하는 기본 연산들을
대체할 수 있다.
국가 또는 화폐에 따라 다른 표현 방식을 커버하기 위해 팩토리 메서드 패턴으로
디자인되어 있다.
NumberFormat
을 구현하는 클래스로는 DecimalFormat
, ExponentialFormat
등이 있다.
public static void main(String[] args) {
// 팩토리 메서드로 구현체를 생성하여 반환
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
double x = 0.2;
System.out.println(currencyFormatter.format(x)); // $0.20를 출력한다.
System.out.println(percentFormatter.format(x)); // 20%를 출력한다.
}
getInstance()
를 호출할 때마다 새로운 Calendar
객체가 생성. 그레고리안, 줄리안 형식이 있는데, 이 두 가지 경우를 모두 커버하기 위해 팩토리 메서드 패턴으로
디자인 되었다.