...
Dynamic Factory Pattern
Dynamic Factory 변형 패턴은 GOF의 Factory Method 패턴의 단점을 보완하기 위한 패턴이다.
기존 팩토리 메서드 패턴
오리지날 Factory Method 패턴의 가장 큰 단점은 제품 객체의 갯수마다 공장 서브 클래스를 1:1 매칭으로 모두 구현해야 된다는 점이다. 그래서 제품 객체가 50개면 공장 객체도 50개를 구현해야 한다. 이는 곧 클래스 폭발로 이어지며 코드 복잡도를 증가시킨다.
interface Shape {
void setColor(String color);
void draw();
}
class Rectangle implements Shape {
String color;
public void setColor(String color) {
this.color = color;
}
public void draw() {
System.out.println("rectangle draw with " + color);
}
}
class Circle implements Shape {
String color;
public void setColor(String color) {
this.color = color;
}
public void draw() {
System.out.println("circle draw with " + color);
}
}
interface ShapeFactory {
default Shape create(String color) {
Shape shape = createShape();
shape.setColor(color);
return shape;
}
// 팩토리 메서드
Shape createShape();
}
class RectangleFactory implements ShapeFactory {
@Override
public Shape createShape() {
return new Rectangle();
}
}
class CircleFactory implements ShapeFactory {
@Override
public Shape createShape() {
return new Circle();
}
}
class Client {
public static void main(String[] args) {
Shape rectangle = new RectangleFactory().create("red");
rectangle.draw();
Shape circle = new CircleFactory().create("yellow");
circle.draw();
}
}
만일 위와 같은 팩토레 메서드 구조에서 Shape 자식 객체를 더 확장할 일이 생겼다고 하자.
그러면 아래 다이어그램 같이 추가한 Shape 자식 구현체만큼 팩토리 객체도 똑같이 늘어나 결과적으로 코드 라인수가 엄청 늘어나게 될 것이다.
물론 팩토리 메서드 패턴이 OCP 원칙을 준수하기 때문에, 수정에 닫히고 확장에 열리긴 하지만 그래도 코드가 무한히 길어지는건 조금 불편하긴 하다.
다이나믹 팩토리 패턴 적용
이를 해결하기위한 또다른 방법으로는 Enum Factory Method 패턴이 존재하지만 Enum 객체는 상속이 안된다는 한계가 존재하기 때문에 사용처가 제한이 된다.
따라서 자바의 Class 클래스를 이용한 Reflection API 기법을 이용하여 유형을 동적으로 등록하고 인스턴스를 초기화 하는 패턴이 바로 Dynamic Factory 패턴이다.
이를 이용해 팩토리 메서드의 서브 클래싱 부피가 늘어나는 한계를 해결 할 수 있다.
class DynamicShapeFactory {
// Class 클래스 객체와 이를 구분하는 이니셜 문자열을 저장하는 HashMap 컬렉션
private static final Map<String, Class<? extends Shape>> registeredTypes = new HashMap<>();
// 팩토리 객체가 생성됨에 동시에 미리 등록
static {
registeredTypes.put("Rectangle", Rectangle.class);
registeredTypes.put("Circle", Circle.class);
}
// 나중에 등록할때 사용하는 메서드
public static void registerType(String type, Class<? extends Shape> cls) {
registeredTypes.put(type, cls);
}
public static Shape create(String type, String color) {
Shape shape = null;
try {
shape = getShape(type);
shape.setColor(color);
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return shape;
}
// 다이나믹 팩토리 메서드
private static Shape getShape(String type) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1. 원하는 제품명의 Class 클래스 객체를 가져옴
Class<?> cls = registeredTypes.get(type);
// 2. 제품 객체의 생성자를 가져옴
Constructor<?> shapeconstructor = cls.getDeclaredConstructor();
// 3. Reflection API를 통해 인스턴스화를 하고 업캐스팅 하여 반환
return (Shape) shapeconstructor.newInstance();
}
}
class Client {
public static void main(String[] args) {
// 다이나믹 팩토리 클래스에 미리 등록된 제품 객체를 가져옴
Shape rectangle = DynamicShapeFactory.create("Rectangle", "red");
rectangle.draw();
Shape circle = DynamicShapeFactory.create("Circle", "yellow");
circle.draw();
// 다이나믹 팩토리 클래스에 동적으로 제품 객체를 등록
DynamicShapeFactory.registerType("Triangle", Triangle.class);
Shape triangle = DynamicShapeFactory.create("Triangle", "green");
triangle.draw();
}
}
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.