1. 팩토리 메소드 패턴이란
GoF의 디자인 패턴에서 제시한 디자인 패턴 중 생성 패턴에 속한다. 해당 서적에서는 팩토리 메소드 패턴을 이렇게 정의하고 있다.
객체를 생성하기 위한 인터페이스를 정의하고,
어떤 클래스의 인스턴스를 생성할지에 대한 처리는 서브클래스가 결정하는 디자인 패턴
요약해서 말하자면, 객체 생성을 대신 수행해주는 일종의 팩토리가 존재하는 것이다. 이때 객체를 생성하는 메소드를 팩토리 메소드라고 하며, 이를 구현 팩토리 객체에서 Override하여 원하는 객체를 생성하게 된다. 클라이언트에서 직접 객체를 생성하는 것이 아니라, 간접적으로 객체를 생성한 뒤 반환해주는 방식이다.
즉, 해당 디자인 패턴은 상위 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며, 하위 클래스가 직접 어떤 객체 인스턴스를 생성할지 결정하게 된다. 이를 UML 형태로 나타내면 아래의 그림과 같다.
이때 Creator의 factoryMethod()는 추상 메소드이며, 이를 ConcreteCreator에서 Override하여 구현 메소드를 작성한다. 결국 실질적으로는 ConcreteCreator를 통해서 Client에게 Product를 생성하여 전달하게 된다.
2. 팩토리 메소드 패턴의 사용
팩토리 메소드 패턴을 사용하는 주된 이유는 클래스의 생성과 사용의 처리 로직을 분리하여 결합도를 낮추기 위해서이다. 팩토리 메소드 패턴을 사용하여 직접 객체를 생성해 사용하는 것을 방지하고, 서브클래스에 생성 로직을 위임함으로써 보다 효율적으로 코드를 제어할 수 있고, 의존성을 제거해 주는 역할을 한다.
또한, 객체 생성을 캡슐화하고 한 곳(팩토리 클래스)에서 관리하기 때문에 수정에는 닫혀 있고, 객체의 자료형이 하위클래스에 의하여 결정된다는 점 때문에 확장이 용이하다는 장점이 있다. 이는 객체지향의 SOLID 원칙 중 OCP에 해당한다.
3. 팩토리 메소드 패턴 예제 코드(by. Java)
Computer 추상 클래스
abstract class Computer {
public void on() { // 컴퓨터 전원버튼으로 켜기 }
public void off() { // 컴퓨터 전원버튼으로 끄기 }
abstract void compose(); // 컴퓨터 조립하는 방법
abstract void use(); // 컴퓨터 사용하는 방법
}
Computer는 on, off, compose, use라는 공통적인 메소드를 갖는 인터페이스이다. 컴퓨터라는 종류로 추상화되어 있는 상태이다. 이때, on과 off 메소드의 경우 어느 컴퓨터든 전원버튼을 이용하여 켜고 끈다는 동일한 행위를 수행하기 때문에 구체 클래스에서의 구현을 줄이기 위해 미리 구현해 두었다.
Computer 구체 클래스
public class Macbook extends Computer {
@Override
void compose() { // 맥북이 조립되어 나오는 과정 }
@Override
void use() { // 트랙패드로 사용하기 }
}
public class Galaxy extends Computer {
@Override
void compose() { // 갤럭시북이 조립되어 나오는 과정 }
@Override
void use() { // 360도로 꺾어서 사용하기 }
}
public class Asus extends Computer {
@Override
void compose() { // Asus 노트북이 조립되어 나오는 과정 }
@Override
void use() { // 평범하게 마우스로 사용하기 }
}
public class Lenova extends Computer {
@Override
void compose() { // Lenova 노트북이 조립되어 나오는 과정 }
@Override
void use() { // 빨콩으로 사용하기 }
}
Computer라는 추상화된 인터페이스를 활용하여 구현한 구체 클래스이다. 각 클래스마다 조립되어 나오는 과정과 사용 방식을 구현해 둔 상태이다.
ComputerFactory 추상 클래스
abstract class ComputerFactory {
public Computer orderComputer(String type){
Computer computer = createComputer(String); // Factory Method
computer.compose();
computer.on();
computer.off();
return computer;
}
abstract Computer createComputer(String type); // Factory Method
}
ComputerFactory는 위의 그림에서 Creator 역할을 한다. 이때 createComputer 부분이 팩토리 메소드가 되고, 이 부분을 구체 클래스에서 실질적으로 구현하여 어떤 객체 인스턴스를 반환할지 결정하게 된다. 그리고 추상 객체에서는 orderComputer 메소드 내부에서 팩토리 메소드를 활용하여 Computer를 생성하고, 기능들을 사용하고 있다.
ComputerFactory 구체 클래스
class ComputerFactoryImpl extends ComputerFactory {
@Override
Computer createComputer(String type) {
if ("Macbook".equals(type)) {
return new Macbook();
} else if ("Galaxy".equals(type)) {
return new Galaxy();
} else if ("Asus".equals(type)) {
return new Asus();
} else if ("Lenova".equals(type)) {
return new Lenova();
}
return null;
}
}
위 두 클래스는 ComputerFactory를 구현한 구체 클래스이다. 객체의 종류가 추가되는 경우, 구체 클래스에 추가해줌으로써 새로운 객체를 반환할 수 있다. 기능의 추가가 필요한 경우 해당 구체 클래스만 변경하고, 추상 클래스는 변경하지 않아도 된다.
팩토리 메소드에 String 값을 인자로 받아 리턴되는 객체의 종류를 변화시킨다. 해당 구체 클래스를 실제로 클라이언트에서 사용함으로써 클라이언트 단에서 리턴되는 객체의 종류를 제어할 수 있다.
클라이언트 코드
public class FactoryPatternTest {
public static void main(String[] args) {
ComputerFactory computerFactory = new ComputerFactoryImpl();
Computer macbook = computerFactory.orderComputer("Macbook");
Computer galaxy = computerFactory.orderComputer("Galaxy");
Computer asus = computerFactory.orderComputer("Asus");
Computer lenova = computerFactory.orderComputer("Lenova");
}
}
위와 같은 형태로 팩토리를 통해 생성되는 객체들의 종류를 제어할 수 있다.