추상팩토리 패턴이란?
추상팩토리 패턴은 구체적인 클래스는 지정하지 않은 채, 관련된 객체들을 생성할 수 있는 디자인 패턴입니다.
이야기로 알아보는 추상팩토리
문제상황
가구점을 운영한다고 가정해 보겠습니다.
가구점에서는 크게 Art Deco, Victorian, Modern 테마로 가구를 만들고 있으며, 가구점에서는 의자, 소파, 커피테이블을 만듭니다.
각 가구들은 같은 테마로 구매를 해야 서로 잘 어울리는 느낌이 납니다. 다른 테마로 구매를 하면 서로 잘 어울리지 않아 고객들의 불만이 많습니다.
또한 새 제품 혹은 제품군을 프로그램에 추가 시 기존 코드를 변경하고 싶지 않을 것입니다.
이럴 때 적용 할 수 있는 디자인 패턴입니다.
해결책 1
우선적으로 의자의 경우 인터페이스로 두고, 각 테마마다 이 인터페이스를 구현하도록 설계를 할 수 있습니다.
해결책 2
그런 다음 제품을 생성하는 공정을 인터페이스로 두고, 각 테마마다 이를 구현해 제품을 생성하는 공정을 구체활 할 수 있습니다.
이렇게 테마마다 공정을 분리하게 될 경우, 같은 테마를 가진 제품들을 생성할 수 있다는 장점이 생깁니다.
추상팩토리 구조
[AbstractFactory]
- 각 추상 제품을 생성하기 위한 추상메서드들을 선언합니다.
[ConcreteFactory]
- 추상 팩토리에서 선언한 추상메서드를 구현합니다.
- 이때, 반환 타입은 Abstract Product여야 합니다.
[Abstract Product]
- 제품이 가지는 공통된 특성들을 인터페이스화 합니다.
[Concrete Product]
- Abstract Product의 구현체입니다.
코드로 알아보는 추상팩토리
GUI를 담당하는 팩토리가 있다고 가정하겠습니다.
GUI에서 간단하게 버튼 그리고 체크박스를 구현하려고 합니다.
Product1 - Button
public interface Button {
void paint();
}
// --- //
public class MacOSButton implements Button{
@Override
public void paint() {
System.out.println("MacOSButton.paint");
}
}
// --- //
public class WindowsButton implements Button{
@Override
public void paint() {
System.out.println("WindowsButton.paint");
}
}
OS별로 다른 버튼을 구현하고 있습니다.
보다 간단한 예시를 들기 위해 메서드의 구현은 메서드 명을 출력하는 방식으로 구현했습니다.
Product2 - CheckBox
public interface CheckBox {
void paint();
}
// --- //
public class MacOSCheckBox implements CheckBox{
@Override
public void paint() {
System.out.println("MacOSCheckBox.paint");
}
}
// --- //
public class WindowsCheckBox implements CheckBox{
@Override
public void paint() {
System.out.println("WindowsCheckBox.paint");
}
}
OS별로 다른 체크박스를 구현하고 있습니다.
보다 간단한 예시를 들기 위해 메서드의 구현은 메서드 명을 출력하는 방식으로 구현했습니다.
Factory
public interface GUIFactory {
Button createButton();
CheckBox createCheckBox();
}
// --- //
public class MacOSFactory implements GUIFactory{
@Override
public Button createButton() {
return new MacOSButton();
}
@Override
public CheckBox createCheckBox() {
return new MacOSCheckBox();
}
}
// --- //
public class WindowsFactory implements GUIFactory{
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public CheckBox createCheckBox() {
return new WindowsCheckBox();
}
}
추상 팩토리인 GUIFactory의 경우 Abstract Product를 반환하는 메서드를 추상화하였습니다.
추상 팩토리의 각 구현체에서 팩토리의 특성에 맞는 Concrete Product를 반환해 주고 있습니다.
Application
public class Application {
private Button button;
private CheckBox checkBox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkBox = factory.createCheckBox();
}
public void paint() {
button.paint();
checkBox.paint();
}
}
Application 코드에서 Abstract Product에 의존하고 있으며, 생성과 관련된 책임은 추상 팩토리에게 맡기는 것을 확인할 수 있습니다.
Client
public class Demo {
private static Application configureApplication() {
Application app;
GUIFactory factory;
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("mac")) {
factory = new MacOSFactory();
} else {
factory = new WindowsFactory();
}
app = new Application(factory);
return app;
}
public static void main(String[] args) {
Application app = configureApplication();
app.paint();
}
}
Configuration 파일을 적용해 Application에 Concrete Factory의 의존성을 주입해주고 있습니다.
[실행결과]
MacOSButton.paint
MacOSCheckBox.paint
저의 경우 MacOS를 사용하기 때문에 MacOS관련 버튼과 체크박스가 실행된 것을 확인 할 수 있습니다.
언제 적용하면 좋을까요?
- 코드가 관련 제품의 다양한 제품과 함께 작동해야 하는데, 구체적인 클래스에 의존하지 않으려는 경우
- 기본 책임을 모호하게 만드는 일련의 팩토리 메서드가 있는 클래스가 있는 경우
장점 및 단점
장점
- Factory에서 받는 제품이 서로 호환되는지 확인 할 수 있다.
- 구체적인 제품과 클라이언트 코드 간의 강한 결합을 피할 수 있다.
- SRP(단일 책임 원칙) : 제품 생성 코드를 한 곳에 추출해 코드를 지원하게 쉽게 만들 수 있다.
- OCP(개방 폐쇄 원칙) : 기존 클라이언트 코드를 손상시키지 않고, 새로운 변형 제품을 도입할 수 있다.
단점
- 코드가 다소 복잡해질 수 있다.
예제 코드가 필요하신 분들은 다음에서 확인해 보실 수 있습니다.
추상팩토리 예제코드 바로가기
'DesignPatterns' 카테고리의 다른 글
팩토리 메서드란? (Factory Method) (0) | 2023.06.15 |
---|