Search

팩토리 메서드와 추상 팩토리

팩토리 메서드와 추상 팩토리

팩토리 메서드

팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지만, 자식 클래스들이 생성될 객체들의 유형을 변경할 수 있도록 하는 생성 패턴입니다
즉 객체를 생성하는 메소드를 정의해 주지만, 어떤 클래스의 인스턴스를 생성할지는 하위 클래스가 결정하게 됩니다
참고
팩토리 메서드 패턴은 new 대신에 객체를 생성하는 메서드를 이용해야하며, 추상 타입에 생성 객체를 유지해야 합니다.
자식 클래스들은 다른 유형의 제품들을 해당 제품들이 공통 기초 클래스 또는 공통 인터페이스가 있는 경우에만 반환할 수 있습니다
추상 인터페이스에 의존을 해야하며, 구상체(Concrete class)에 의존하면 안됩니다. new를 하게 되면 구체적 클래스에 의존하게 됩니다.

단계

Creator 클래스는 어떤 ConcreteCreator 클래스가 사용될지 모르는 상태에서 작성이 가능합니다
1.
제품 인터페이스 (Product Interface)
팩토리 메서드가 생성할 수 있는 모든 객체의 공통 인터페이스를 정의합니다.
이 인터페이스는 크리에이터와 구상 제품 클래스들 사이의 계약 역할을 합니다.
2.
구상 제품 (Concrete Products)
제품 인터페이스의 다양한 구현체들입니다.
각 구상 제품은 인터페이스를 자신만의 방식으로 구현합니다.
3.
크리에이터 (Creator)
새로운 제품 객체를 반환하는 팩토리 메서드를 선언합니다.
팩토리 메서드의 반환 타입은 반드시 제품 인터페이스와 일치해야 합니다.
주요 특징
크리에이터 클래스는 보통 제품과 관련된 핵심 비즈니스 로직을 포함합니다.
팩토리 메서드의 주요 역할은 이 비즈니스 로직을 구상 제품 클래스들로부터 분리(디커플링)하는 것입니다.
4.
구상 크리에이터 (Concrete Creators)
크리에이터 클래스의 팩토리 메서드를 오버라이드(재정의)합니다.
각 구상 크리에이터는 서로 다른 유형의 구상 제품을 반환하도록 팩토리 메서드를 구현합니다.

구현

크로스 플랫폼 다이얼로그(대화 상자) 예시.
기초 Dialog 클래스 내에서 버튼을 생성하는 팩토리 메서드를 선언하면 나중에 팩토리 메서드에서 윈도우 유형의 버튼들을 반환하는 Dialog 자식 클래스를 생성할 수 있습니다.
그 후 이 자식 클래스는 기초 클래스로부터 Dialog 의 코드 대부분을 상속받으나, 팩토리 메서드 덕분에 윈도우 유형의 버튼들도 렌더링할 수 있습니다
물론, 위 접근 방식을 다른 UI 요소들에도 적용할 수 있으나, 대화 상자에 새로운 팩토리 메서드를 추가할 때마다 이 프로그램은 추상 팩토리 패턴에 더 가까워집니다.
package main import ( "fmt" ) // Button => 인터페이스 type Button interface { Render() OnClick() } // WindowsButton => 구상 구조체 // 구상 제품들은 제품 인터페이스의 다양한 구현을 제공합니다 type WindowsButton struct{} func (b *WindowsButton) Render() { fmt.Println("Window 버튼 렌더링 중") } func (b *WindowsButton) OnClick() { fmt.Println("Window 버튼 클릭") } // HTMLButton => 구상 구조체 type HTMLButton struct{} func (b *HTMLButton) Render() { fmt.Println("HTML 버튼 렌더링 중") } func (b *HTMLButton) OnClick() { fmt.Println("HTML 버튼 클릭") } // Dialog => Creator 인터페이스 type Dialog interface { CreateButton() Button Render() } // WindowsDialog => 구상 구조체 type WindowsDialog struct{} func (d *WindowsDialog) CreateButton() Button { return &WindowsButton{} } func (d *WindowsDialog) Render() { button := d.CreateButton() button.Render() button.OnClick() } // WebDialog => 구상 구조체 type WebDialog struct{} func (d *WebDialog) CreateButton() Button { return &HTMLButton{} } func (d *WebDialog) Render() { button := d.CreateButton() button.Render() button.OnClick() } // Application type Application struct { dialog Dialog } func (app *Application) Initialize(os string) error { switch os { case "Windows": app.dialog = &WindowsDialog{} case "Web": app.dialog = &WebDialog{} default: return fmt.Errorf("error! Unknown operating system: %s", os) } return nil } func (app *Application) Main() { app.dialog.Render() } func main() { app := &Application{} os := "Windows" fmt.Printf("Creating: %s\n", os) err := app.Initialize(os) if err != nil { fmt.Println(err) return } app.Main() }
Go
복사

추상 팩토리

관련 객체들의 Concrete 클래스들을 지정하지 않고도 관련 객체들의 모음을 생성할 수 있도록 하는 생성패턴
팩토리 메서드가 is-a로 단일 제품의 생성을 처리했다면, 추상 팩토리의 경우, has-a 관계를 이용하게 됩니다.
더 나아가, 추상 팩토리는 여러 종류의 관련 객체들을 일관된 방식으로 생성하는 인터페이스를 제공합니다.
그렇기 때문에, 추상 팩토리는 비교적 팩토리 메서드보다 더 복잡한 구조를 가지며, 여러 관련 객체들의 생성을 캡슐화하게 됩니다.
구현
예시
팩토리 메서드
단일 제품의 다양한 변형을 다룰 때 사용합니다 다양한 유형의 버튼(Windows 버튼, MacOS 버튼 등)
추상 팩토리
여러 관련 제품들의 집합을 다룰 때 사용합니다. 예: 가구 세트(의자, 테이블, 소파 등)의 다양한 스타일(모던, 빅토리안, 아트데코 등)
하지만 여기까지만 하면 팩토리 메서드와 추상 팩토리 간 차이가 분명하지 않을 수 있습니다
 팩토리 메서드 → 추상 팩토리가 되거나, 차이를 보이려면 아래 3가지를 만족
1.
특정 그룹에 속하는 여러 객체들을 하나의 팩토리로 묶어서 생성
2.
위 그룹을 만드는 팩토리에 대해 추상화된 팩토리 클래스가 있어야 함
3.
의존성 역전 원칙(Dependency Inversion Principle, DIP)을 적용
추상 팩토리 → 추상 제품 ↑ ↑ 구체 팩토리 → 구체 제품
Go
복사
구체 제품은 추상 팩토리를 모르고, 구체 팩토리는 추상 제품을 모르는 상태
크로스 플랫폼 사용자 인터페이스 클래스 예시.
1.
GUIFactory는 추상 팩토리 인터페이스입니다.
이 인터페이스는 서로 관련된 제품들(Button과 Checkbox)을 생성하는 메서드를 정의합니다.
2.
구상 팩토리 (Concrete Factories)
WinFactoryMacFactory는 구상 팩토리 클래스입니다.
각 구상 팩토리는 GUIFactory 인터페이스를 구현하여 특정 운영 체제에 맞는 UI 요소들을 생성합니다.
3.
추상 제품 (Abstract Products)
ButtonCheckbox는 추상 제품 인터페이스입니다.
이들은 구상 제품들이 구현해야 할 공통 메서드를 정의합니다.
4.
구상 제품 (Concrete Products)
이들은 각 운영 체제에 맞는 구상 제품 클래스입니다.
각 구상 제품은 해당하는 추상 제품 인터페이스를 구현합니다.
5.
client
클라이언트는 추상 팩토리와 추상 제품 인터페이스를 사용하여 작업합니다.

추상 팩토리

여러 추상 메서드를 포함하는 인터페이스를 정의합니다. 각 메서드는 다른 유형의 객체를 생성합니다.
구체적인 팩토리 클래스들이 이 인터페이스를 구현하여 관련된 객체들의 집합(Gui의 집합)을 생성합니다.
package main import ( "AbstactFactory/config" "fmt" "log" ) // GUIFactory 추상 팩토리 인터페이스. (여러 추상 메서드 포함) type GUIFactory interface { CreateButton() Button CreateCheckbox() Checkbox } // Button은 추상 제품 인터페이스. type Button interface { Paint() } // Checkbox는 추상 제품 인터페이스. type Checkbox interface { Paint() } // WinFactory는 구상 팩토리. type WinFactory struct{} func (f *WinFactory) CreateButton() Button { return &WinButton{} } func (f *WinFactory) CreateCheckbox() Checkbox { return &WinCheckbox{} } // MacFactory는 구상 팩토리. type MacFactory struct{} func (f *MacFactory) CreateButton() Button { return &MacButton{} } func (f *MacFactory) CreateCheckbox() Checkbox { return &MacCheckbox{} } // WinButton은 구상 제품. type WinButton struct{} func (b *WinButton) Paint() { fmt.Println("Rendering a button in Windows style") } // WinCheckbox는 구상 제품. type WinCheckbox struct{} func (c *WinCheckbox) Paint() { fmt.Println("Rendering a checkbox in Windows style") } // MacButton은 구상 제품. type MacButton struct{} func (b *MacButton) Paint() { fmt.Println("Rendering a button in Mac style") } // MacCheckbox는 구상 제품. type MacCheckbox struct{} func (c *MacCheckbox) Paint() { fmt.Println("Rendering a checkbox in Mac style") } // Application은 클라이언트 코드. type Application struct { factory GUIFactory button Button } func NewApplication(factory GUIFactory) *Application { return &Application{factory: factory} } func (a *Application) CreateUI() { a.button = a.factory.CreateButton() } func (a *Application) Paint() { a.button.Paint() } // 설정에 따라 적절한 팩토리를 생성. func createFactory(os string) (GUIFactory, error) { // if 문도 가능하지만, switch 문이 더 깔끔 switch os { case "Windows": return &WinFactory{}, nil case "Mac": return &MacFactory{}, nil default: return nil, fmt.Errorf("Error! Unknown operating system: %s", os) } } func main() { cfg, err := config.LoadConfig("config.json") if err != nil { log.Fatalf("Error loading config: %v", err) } factory, err := createFactory(cfg.OS) if err != nil { log.Fatalf("Error creating factory: %v", err) } app := NewApplication(factory) app.CreateUI() app.Paint() }
Go
복사