샘성의 iOS 개발 일지

[디자인 패턴] 추상 팩토리 패턴 본문

iOS/HIG & 디자인 패턴

[디자인 패턴] 추상 팩토리 패턴

SamusesApple 2024. 6. 18. 11:44
728x90

서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공하는 생성자 패턴

 

팩토리 메서드 패턴과의 차이점?

 

[디자인 패턴] 팩토리 메서드 패턴

객체 생성을 캡슐화 하여 ‘팩토리 클래스’를 통해 생성하도록 하는 생성 디자인 패턴 구조CreatorProduct 객체를 생성하는 메서드 정의 (protocol / class)Concrete CreatorConcrete Product를 생성하기 위한 구

iossammy.tistory.com

 

  • 팩토리 매서드
    • 1 팩토리 - 1 객체 생성
    • 구체적 객체 생성 과정을 하위 클래스에 구현하도록 하는 것이 point
    • → 메서드 레벨에서의 구체화된 인스턴스의 생성 및 구성에 대한 의존성 감소
  • 추상 팩토리
    • 1 팩토리 - 연관된 여러 종류의 객체 생성
    • 연관된 여러 종류 객체의 묶음을 구체적인 클래스에 의존하지 않고 생성 가능하도록 하는 것이 point
      • 클래스 레벨에서의 구체화된 객체의 생성 및 구성에 대한 의존성 감소

 


 

Swift 예제 코드

/*
 [배경]
    이전 View에서 **애플 키보드 마우스 세트** or **윈도우 키보드 마우스 세트**를 유저가 선택 할 수 있는 상황
    이전 View에서 어떤 세트를 선택했느냐에 따라 결제 View를 다르게 띄워줘야 한다.
 */

enum TechBrand: CaseIterable {
    case apple
    case window
}

// 추상 팩토리 인터페이스 - 연관된 추상 제품들을 반환하는 메서드들의 집합
protocol KeyboardMouseFactory {
    func createMouse() -> Mouse
    func createKeyboard() -> Keyboard
}

struct AppleKeyboardMouseFactory: KeyboardMouseFactory {
    func createMouse() -> any Mouse {
        return AppleMouse()
    }
    
    func createKeyboard() -> any Keyboard {
        return AppleKeyboard()
    }
}

struct WindowKeyboardMouseFactory: KeyboardMouseFactory {
    func createMouse() -> any Mouse {
        return WindowMouse()
    }
    
    func createKeyboard() -> any Keyboard {
        return WindowKeyboard()
    }
}

// 이미 선택된 TechBrand에 따라 다른 '키보드 + 마우스 세트'를 보여줘야하는 결제 화면
final class PurchaseViewController: UIViewController {
    
    private var keyboardMouseFactory: KeyboardMouseFactory
    
    private lazy var keyboard: Keyboard = keyboardMouseFactory.createKeyboard()
    private lazy var mouse: Mouse = keyboardMouseFactory.createMouse()
    
    // 팩토리 유형이 선택된 TechBrand에 따라 유연하게 바뀐다.
    init(selected type: TechBrand) {
        switch type {
        case .apple:
            self.keyboardMouseFactory = AppleKeyboardMouseFactory()
        case .window:
            self.keyboardMouseFactory = WindowKeyboardMouseFactory()
        }
    }
    
    .
    .
    .
    
    
    func getTotalPrice() {
        print(self.keyboard.price + self.mouse.price)
    }
}

// 마우스
protocol Mouse {
    var price: Int { get }
    
    func click()
    func scroll()
}

struct AppleMouse: Mouse {
    
    var price: Int = 70000
    
    func click() {
        print("애플 마우스 - 클릭")
    }
    
    func scroll() {
        print("애플 마우스 - 스크롤")
    }
}

struct WindowMouse: Mouse {
    
    var price: Int = 50000
    
    func click() {
        print("윈도우 마우스 - 클릭")
    }
    
    func scroll() {
        print("윈도우 마우스 - 스크롤")
    }
}

// 키보드
protocol Keyboard {
    
    var price: Int { get }
    
    func type(_ letter: String)
}

struct AppleKeyboard: Keyboard {
    
    var price: Int = 130000
    
    func type(_ letter: String) {
        print("애플 키보드 -'\\(letter)'")
    }
    
}

struct WindowKeyboard: Keyboard {
    
    var price: Int = 110000
    
    func type(_ letter: String) {
        print("윈도우 키보드 -'\\(letter)'")
    }
    
}

// 1. 애플 세트 선택
let applePurchaseViewController = PurchaseViewController(selected: .apple)

applePurchaseViewController.getTotalPrice() >> 200000

// 2. 윈도우 세트 선택
let windowPurchaseViewController = PurchaseViewController(selected: .window)

windowPurchaseViewController.getTotalPrice() >> 160000

 


언제 사용하면 좋을까?

💡 객체 생성 과정을 추상화하여 코드의 유연성과 유지보수성을 높이고 결합도를 낮추는 데 큰 장점이 있다

 

  • 클라이언트 단에서 구체적인 클래스를 모를 때
  • 서로 연관된 여러 객체들을 함께 생성해야 할 때

 

주의 할 점

  • 추상 팩토리 인터페이스에 연관되는 객체가 새로 추가 될 때마다 바꿔야 한다.
    • e.g.) KeyboardMouseFactory에 ‘키링’이 추가 된 경우, AppleKeyboardMouseFactory, WindowKeyboardMouseFactory에 키링에 관련된 코드를 추가해야한다. → 번거로움

 

 

 

참고:

https://inpa.tistory.com/entry/GOF-💠-추상-팩토리Abstract-Factory-패턴-제대로-배워보자

https://refactoring.guru/ko/design-patterns/abstract-factory

728x90