_Rey
프로그래뭄
_Rey
전체 방문자
오늘
어제
  • 분류 전체보기 (118)
    • Life (2)
    • iOS (49)
      • iOS (13)
      • Swift (19)
      • UIKit (0)
      • RxSwift (6)
      • SwiftUI (11)
    • Design Pattern (14)
      • GoF - Creational Patterns (6)
      • GoF - Structural Patterns (7)
    • Data Structure & Algorithm (48)
      • Data Structure (3)
      • Algorithm (8)
      • Advent of Code 2024 (1)
      • 코테 스터디 TIL (36)
    • English (2)
    • Book (2)
      • Clean Architecture (2)

블로그 메뉴

  • Instagram
  • Github
  • Naver Blog

공지사항

  • Hello, Programoom

인기 글

hELLO · Designed By 정상우.
_Rey

프로그래뭄

[GoF Design Patterns] Factory Method
Design Pattern/GoF - Creational Patterns

[GoF Design Patterns] Factory Method

2024. 9. 24. 17:21

* 예제 코드 및 설명에 필요한 개념들이 Swift를 기준으로 작성된 글입니다.

 

GoF 디자인 패턴 첫번째 키워드는 Creational Patterns 중 Factory Method 입니다.

Creational Patterns으로는 Factory Method, Abstract Factory, Builder, Prototype, Singleton 이 있습니다.

Creational Patterns은 코드의 유연성과 재사용성을 증가시키기 위한 객체 생성 패턴입니다.

1. 설계 목적

부모 클래스에서 객체를 생성하기 위한 인터페이스를 제공하지만, 자식 클래스에서 생성될 객체의 유형을 변경할 수 있도록 패턴입니다.

역시 한 번에 이해가 되지 않습니다.

2. 문제 상황

이해를 위해 사이트에서 제공하는 문제 상황을 살펴봅시다.

당신이 물류 관리 앱을 개발하고 있다고 가정합시다. 앱의 첫 번째 버전은 트럭 운송만 처리할 수 있어서 대부분의 코드가 Truck(트럭) 클래스에 있습니다.

출처: https://refactoring.guru/design-patterns/factory-method

그런데 만약 앱에 Ship을 추가하게 되었다면 어떻게 해야할까요?

이미 Truck 클래스로 결합된 코드를 전부 수정해야할지도 모릅니다. 또한 이후에 어떤 교통수단이 다시 추가될지도 알 수 없습니다.

3. 해결책

이러한 상황에서 Factory Method를 활용하여 문제를 해결할 수 있습니다.

필요한 클래스의 생성자를 Protocol을 통해 호출합니다.

출처: https://refactoring.guru/design-patterns/factory-method

예를 들어 Truck 클래스는 RoadLogistics에서 Ship 클래스는 SeaLogisitics에서 생성되어 반환됩니다.

protocol Logistics {
    // Transport를 생성할 함수 
    func createTransport() -> Transport
    // Logistics에서 Transport의 로직을 담은 함수
    func planDelivery()
}

extension Logistics {
    func planDelivery() {
        let transport = createTransport()
        transport.deliver()
    }
}

class RoadLogistics: Logistics {
    func createTransport() -> Transport {
        return Truck()
    }
}

class SeaLogistics: Logistics {
    func createTransport() -> Transport {
        return Ship()
    }
}

 

먼저 공통되는 Logistics 프로토콜을 정의합니다.

RoadLogistic와 SeaLogistics 클래스는 Logistics 프로토콜을 체택하여 각 클래스에 맞는 Transport 생성할 수 있도록 합니다.

출처: https://refactoring.guru/design-patterns/factory-method

protocol Transport {
    func deliver()
}

class Truck: Transport {
    func deliver() {
        print("Deliver by land in a box.")
    }
}

class Ship: Transport {
    func deliver() {
        print("Deliver by sea in a container.")
    }
}

Transport 프로토콜을 정의하고, Truck과 Ship 클래스가 채택합니다.

각 클래스에 맞게 deliver() 함수를 정의합니다.

4. 구조

출처: https://refactoring.guru/design-patterns/factory-method

구조에 대해 그림으로 표시하면 다음과 같습니다.

Creator = Logistics, ConcreteCreatorA = RoadLogistics, ConcreteCreatorB = SeaLogistics

Product = Transport, ConcreteProductA = Truck, ConcreteProductB = Ship

이 됩니다.

5. 예제 코드의 사용

원활한 사용을 위해 enum을 추가했습니다.

enum LogisticsType {
    case road
    case sea
}

class LogisticsCompany {
    private var logistics: Logistics

    init(logisticsType: LogisticsType) {
        self.logistics = {
            switch logisticsType {
            case .road: return RoadLogistics()
            case .sea: return SeaLogistics()
            }
        }()
    }

    func deliverState() {
        logistics.planDelivery()
    }
}

let roadLogisticsCompany = LogisticsCompany(logisticsType: .road)
roadLogisticsCompany.deliverState()
// Deliver by land in a box.

let seaLogisticsCompany = LogisticsCompany(logisticsType: .sea)
seaLogisticsCompany.deliverState()
// Deliver by sea in a container.

 

위 코드에서 LogisticsCompany처럼 해당 코드를 사용하는 클래스를 클라이언트 코드라고 합니다.

6. 장단점

Creator와 Product의 결합도가 낮아집니다.

또한 SOLID 원칙 중 Single Responsibility Principle(SRP)과 Open/Closed Principle(OCP) 원칙을 준수하게 됩니다.

하지만 오히려 너무 많은 자식 클래스 종류가 필요할 경우 코드가 복잡해질 수 있습니다.

7. 실제 사용 사례

만약 다크모드, 라이트모드에 따라서 버튼의 모양을 달리하고 싶다고 어떻게 해야할까요?

다크모드에서는 좀더 샤프하게 직각 모서리를, 라이트모드에서는 둥근 모서리를 적용해야한다고 가정해봅시다.

// Product Protocol
protocol CustomButton: UIButton {
    func configure()
}

// Product A
class LightModeButton: UIButton, CustomButton {
    func configure() {
        self.backgroundColor = .white
        self.layer.cornerRadius = 10
        self.setTitleColor(.black, for: .normal)
        self.setTitle("Light Mode Button", for: .normal)
    }
}

// Product B
class DarkModeButton: UIButton, CustomButton {
    func configure() {
        self.backgroundColor = .black
        self.setTitleColor(.white, for: .normal)
        self.setTitle("Dark Mode Button", for: .normal)
    }
}

 

먼저 각 버튼에 사용할 프로토콜과 버튼 클래스를 정의합니다.

// Creator Protocol
protocol ButtonFactory {
    func createButton() -> CustomButton
}

// Product A Creator
class LightButtonFactory: ButtonFactory {
    func createButton() -> CustomButton {
        return LightModeButton()
    }
}

// Product B Creator
class DarkButtonFactory: ButtonFactory {
    func createButton() -> CustomButton {
        return DarkModeButton()
    }
}

 

Factory Method 패턴으로 생성자 클래스를 정의합니다.

class SettingsViewController: UIViewController {
    var buttonFactory: ButtonFactory?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        buttonFactory = {
            switch traitCollection.userInterfaceStyle {
            case .dark: return DarkButtonFactory()
            default: return LightButtonFactory()
            }
        }()
    
        if let button = buttonFactory?.createButton() {
            button.configure()
            view.addSubview(button)
            button.frame = CGRect(x: 100, y: 100, width: 200, height: 50)
        }
    }
}

 

클라이언트 코드에서 상황에 맞는 버튼를 생성하고 사용합니다.

이 과정에서 버튼의 생성 로직과 클라이언트 코드의 결합도가 낮아집니다.

 

8. Conclusion

Factory Method 패턴은 객체 생성을 위임하는 패턴이라고도 볼 수 있겠습니다.

확실히 코드간의 결합도를 낮춰 유연성을 증가시킬 수 있겠습니다.

Reference

https://refactoring.guru/design-patterns/factory-method

저작자표시 비영리 변경금지 (새창열림)

'Design Pattern > GoF - Creational Patterns' 카테고리의 다른 글

[GoF Design Patterns] Singleton  (0) 2024.10.18
[GoF Design Patterns] Prototype  (0) 2024.10.01
[GoF Design Patterns] Builder  (0) 2024.09.28
[GoF Design Patterns] Abstract Factory - 실제 사용 예제  (1) 2024.09.26
[GoF Design Patterns] Abstract Factory  (0) 2024.09.26
    'Design Pattern/GoF - Creational Patterns' 카테고리의 다른 글
    • [GoF Design Patterns] Prototype
    • [GoF Design Patterns] Builder
    • [GoF Design Patterns] Abstract Factory - 실제 사용 예제
    • [GoF Design Patterns] Abstract Factory
    _Rey
    _Rey
    잘 배워서 다 남주자

    티스토리툴바