728x90
생성 패턴 : 팩토리 메서드 패턴
팩토리 메서드 패턴이란?
- 생성과 구현의 분리
- 객체 생성을 위한 인터페이스(or 상위 클래스)를 정의하고, 실제 어떤 클래스가 생성될지는 구현 클래스(or 하위 클래스)가 결정한다.
- 어떤 구현 객체가 실제 생성되었는지
Client
는 알지 못한다. (결합도가 낮다.)
팩토리 메서드 패턴이 필요한 이유
- 모든 코드가 특정 객체에 결합도가 높게 작성되었다면, 다른 객체가 해당 객체를 대체하거나 새로운 객체를 포함할 때 많은 코드를 변경해야 한다.
- 공통 로직마다 구현체별로 분기 처리가 필요하다.
- 팩토리 메서드 패턴을 이용하면 Client는 인터페이스만 참조하고 특정 구현체에 종속되어있지 않으므로 확장성이 커진다.
- 새로운 객체가 추가되거나 대체되더라도 인터페이스가 변하지 않는 이상 다른 코드에 영향을 주지 않는다. (
OCP
)
- 새로운 객체가 추가되거나 대체되더라도 인터페이스가 변하지 않는 이상 다른 코드에 영향을 주지 않는다. (
팩토리 메서드 특징
- 모든 구현체들은 특정 인터페이스를 공통적으로 따라야 한다.
구조
Creator
- 인터페이스 또는 상위 클래스
- 객체를 반환하는 방법을 정의한다.
ConcreateCreator
- Creator 구현체
- 특정 객체를 생성하여 반환한다.
Product
- 생성할 객체의 인터페이스
ConcreteProduct
- 생성할 객체의 실제 구현체
예제 코드
상속을 이용한 예제 코드
guru 사이트의 수도 코드를 java로 변경하였습니다.
// Creator
abstract class Dialog {
// 팩토리 메서드 (추상 메서드)
abstract Button createButton();
public void render() {
Button okButton = createButton();
okButton.onClick(() -> closeDialog());
okButton.render();
}
private void closeDialog() {
System.out.println("Dialog closed");
}
}
// Concrete creator
class WindowsDialog extends Dialog {
@Override
Button createButton() {
return new WindowsButton();
}
}
class WebDialog extends Dialog {
@Override
Button createButton() {
return new HTMLButton();
}
}
// Product (interface)
interface Button {
void render();
void onClick(Runnable f);
}
// Concrete product
class WindowsButton implements Button {
@Override
public void render() {
System.out.println("Render Windows button");
}
@Override
public void onClick(Runnable f) {
System.out.println("Bind native Windows click event");
f.run();
}
}
class HTMLButton implements Button {
@Override
public void render() {
System.out.println("Render HTML button");
}
@Override
public void onClick(Runnable f) {
System.out.println("Bind web browser click event");
f.run();
}
}
class Config {
private String OS;
public Config(String OS) {
this.OS = OS;
}
public String getOS() {
return OS;
}
}
// 실행 부분
class Application {
private Dialog dialog;
public void initialize() throws Exception {
Config config = readApplicationConfigFile();
if (config.getOS().equals("Windows")) {
dialog = new WindowsDialog();
} else if (config.getOS().equals("Web")) {
dialog = new WebDialog();
} else {
throw new Exception("Error! Unknown operating system.");
}
}
private Config readApplicationConfigFile() {
return new Config("Windows");
}
public void main() {
try {
initialize();
dialog.render();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class FactoryMethodDemo {
public static void main(String[] args) {
Application app = new Application();
app.main();
}
}
Refactoring Guru 사이트 예제에서는 상속을 이용했지만, 합성을 이용하면 더 유연하기 때문에
합성으로 구현해봅시다.
합성으로 구현
// Creator
interface ButtonFactory {
// 팩토리 메서드
Button createButton();
}
// Concrete Creator
class WindowsButtonFactory implements ButtonFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
}
class WebButtonFactory implements ButtonFactory {
@Override
public Button createButton() {
return new HTMLButton();
}
}
// Product
interface Button {
void render();
void onClick(Runnable f);
}
// Concrete Product
class WindowsButton implements Button {
@Override
public void render() { }
@Override
public void onClick(Runnable f) { }
}
class HTMLButton implements Button {
@Override
public void render() { }
@Override
public void onClick(Runnable f) { }
}
// Dialog는 이제 Creator가 아닌 Client가 됨
class Dialog {
private final ButtonFactory buttonFactory; // Creator를 합성으로 가진다.
public Dialog(ButtonFactory buttonFactory) {
this.buttonFactory = buttonFactory;
}
public void render() {
Button button = buttonFactory.createButton();
button.onClick(() -> closeDialog());
button.render();
}
private void closeDialog() { }
}
- 상속으로 변경하면 런타임에 구현체인 Factory를 변경할 수 있다.
- Dialog에서 더이상 버튼 생성 책임을 가지지 않아 단일 책임 원칙(SRP)을 더 잘 지킨다.
적용점
- 코드에서 사용되는 객체의 정확한 유형과 종속성을 알지 못할 경우 사용한다.
- 실제 구현체와 느슨하게 결합하여 확장성이 높다.
- 라이브러리 또는 프레임워크 사용자에게 내부 구성 요소를 확장 가능하도록 만들고 싶을 때 사용한다.
- 생성 비용이 무거울 경우, 시스템 리소스를 재사용할 때 사용 가능하다.
- 매번 객체를 생성하지 않고, 팩토리에서 가져와 사용할 수 있다.
class DatabaseConnectionFactory {
// 팩토리 메서드
public DatabaseConnection getConnection() {
// 1. 사용 가능한 기존 연결이 있는지 확인
for (DatabaseConnection conn : connectionPool) {
if (!conn.isInUse()) {
conn.setInUse(true);
return conn;
}
}
// 2. 새 연결 생성하기/..
}
}
Reference
https://refactoring.guru/design-patterns/factory-method
Factory Method
/ Design Patterns / Creational Patterns Factory Method Also known as: Virtual Constructor Intent Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objec
refactoring.guru
728x90
'설계 > 디자인 패턴' 카테고리의 다른 글
[디자인패턴] 생성 패턴 - 빌더 패턴 (0) | 2025.02.14 |
---|---|
[디자인패턴] 생성 패턴 : 추상 팩토리 패턴 (0) | 2025.02.13 |
[디자인 패턴] 디자인 패턴이란? (0) | 2025.02.11 |