apple/DesignPattern, Architecture

Swift 디자인패턴 Factory Pattern (팩토리 패턴)

lgvv 2022. 5. 8. 15:05

Swift 디자인패턴 Factory Pattern (팩토리 패턴)

 

Factory Pattern은 객체 생성을 캡슐화하여, 클라이언트가 객체 생성 방식에 대해 알 필요 없도록 설계하는 생성 패턴

이를 통해 객체 생성 로직을 중앙 집중화하고, 유지보수를 용이하게 함.

 

히스토리

    • 2022-05-08: 디자인 패턴 스터디 정리
    • 2024-11-28: 포스팅 글 재정리 및 예제 변경

팩토리 패턴

 

Factory Pattern

팩토리 패턴은 다른 개념으로도 확장 가능

  • 단순 팩토리 패턴: 조건에 따라 객체를 생성하는 메서드만 제공 (정식 팩토리 패턴은 아님)
  • 팩토리 메서드 (Factory Method) 패턴: 서브클래스에서 객체 생성 과정을 오버라이드하여 생성 로직을 확장
  • 추상 팩토리 (Abstract Factory) 패턴: 관련 객체군을 생성하기 위한 인터페이스를 제공해, 구체적인 객체 없이 생성 가능

팩토리 패턴 장점

  • 객체 생성 로직의 재사용성 증가
  • 코드 유지보수성 향상
  • 클라이언트 객체 생성 로직 간의 느슨한 결합 제공

단점

  • 클래스 계층이 깊어질 경우 코드 복잡도 증가할 수 있음

 

코드 예시 

팩토리 패턴을 활용해 동아리 지원자 시스템 구축 해보기

import SwiftUI

struct Applicant {
    var name: String
    var email: String
    var phase: Phase
    
    enum Phase {
        case applied          // 지원 완료
        case assignment       // 사전과제
        case interview        // 인터뷰
        case accepted         // 합격
        case rejected         // 탈락
    }
}

struct Email {
    var subject: String
    var message: String
}

struct EmailFacotry {
    
    func create(applicant: Applicant) -> Email {
        switch applicant.phase {
        case .applied:
            return .init(subject: "서류 제출 완료", message: "제출 완료 되었습니다")
        case .assignment:
            return .init(subject: "사전과제 안내", message: "사전과제 해주세요")
        case .interview:
            return .init(subject: "면접 안내", message: "화상면접 들어와주세요")
        case .accepted:
            return .init(subject: "합격 안내", message: "최종합격 했어요")
        case .rejected:
            return .init(subject: "불합격 안내", message: "다음기회에 다시 지원해주세요")
        }
    }
}

private struct ContentView: View {
    var body: some View {
        Button("Execute") {
            let facotry = EmailFacotry()
            var applicant = Applicant(
                name: "name",
                email: "email@example.com",
                phase: .applied
            )
            print(facotry.create(applicant: applicant))
            applicant.phase = .assignment
            print(facotry.create(applicant: applicant))
            applicant.phase = .interview
            print(facotry.create(applicant: applicant))
            applicant.phase = .accepted
            print(facotry.create(applicant: applicant))
            applicant.phase = .rejected
            print(facotry.create(applicant: applicant))
        }
    }
}

#Preview {
    ContentView()
}

결과

 

 

코드 예시 (단순 팩토리 패턴)

import SwiftUI

enum VehicleType {
    case car
    case bike
}

protocol Vehicle {
    func drive()
}

class Car: Vehicle {
    func drive() {
        print("Driving a car!")
    }
}

class Bike: Vehicle {
    func drive() {
        print("Riding a bike!")
    }
}

class VehicleFactory {
    static func createVehicle(type: VehicleType) -> Vehicle {
        switch type {
        case .car:
            return Car()
        case .bike:
            return Bike()
        }
    }
}

private struct ContentView: View {
    var body: some View {
        Button("Execute") {
            let car = VehicleFactory.createVehicle(type: .car)
            car.drive()

        }
    }
}

#Preview {
    ContentView()
}

 

코드 예시 (팩토리 메서드 패턴)

import SwiftUI

protocol Transport {
    func deliver()
}

class Truck: Transport {
    func deliver() {
        print("Delivering by truck!")
    }
}

class Ship: Transport {
    func deliver() {
        print("Delivering by ship!")
    }
}

protocol Logistics {
    func createTransport() -> Transport
}

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

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

private struct ContentView: View {
    var body: some View {
        Button("Execute") {
            // 사용 예시
            let roadLogistics = RoadLogistics()
            let transport = roadLogistics.createTransport()
            transport.deliver() // Output: Delivering by truck!
        }
    }
}

#Preview {
    ContentView()
}

 

코드 예시 (추상 팩토리 패턴)

import SwiftUI

fileprivate protocol Button {
    func render()
}

fileprivate protocol Checkbox {
    func toggle()
}

fileprivate class MacOSButton: Button {
    func render() {
        print("Rendering MacOS button.")
    }
}

fileprivate class MacOSCheckbox: Checkbox {
    func toggle() {
        print("Toggling MacOS checkbox.")
    }
}

fileprivate class WindowsButton: Button {
    func render() {
        print("Rendering Windows button.")
    }
}

fileprivate class WindowsCheckbox: Checkbox {
    func toggle() {
        print("Toggling Windows checkbox.")
    }
}

fileprivate protocol UIFactory {
    func createButton() -> Button
    func createCheckbox() -> Checkbox
}

fileprivate class MacOSFactory: UIFactory {
    func createButton() -> Button {
        return MacOSButton()
    }

    func createCheckbox() -> Checkbox {
        return MacOSCheckbox()
    }
}

fileprivate class WindowsFactory: UIFactory {
    func createButton() -> Button {
        return WindowsButton()
    }

    func createCheckbox() -> Checkbox {
        return WindowsCheckbox()
    }
}

private struct ContentView: View {
    var body: some View {
        SwiftUI.Button("Execute") {
            // 사용 예시
            let factory: UIFactory = MacOSFactory()
            let button = factory.createButton()
            button.render() // Output: Rendering MacOS button.
        }
    }
}

#Preview {
    ContentView()
}

 

(참고)

https://www.raywenderlich.com/books/design-patterns-by-tutorials/v3.0/chapters/11-factory-pattern

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

 

팩토리 메서드 패턴

/ 디자인 패턴들 / 생성 패턴 팩토리 메서드 패턴 다음 이름으로도 불립니다: 가상 생성자, Factory Method 의도 팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지

refactoring.guru

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

 

추상 팩토리 패턴

/ 디자인 패턴들 / 생성 패턴 추상 팩토리 패턴 다음 이름으로도 불립니다: Abstract Factory 의도 추상 팩토리는 관련 객체들의 구상 클래스들을 지정하지 않고도 관련 객체들의 모음을 생성할 수 있

refactoring.guru