본문 바로가기

스위프트

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

팩토리 메서드 패턴은 Creational Pattern 종류 중 하나로,

객체의 생성을 서브클래스가 하도록 하는 패턴

 

팩토리 메서드 패턴은 아래의 4가지 객체를 통해 구현할 수 있다.

 

[1] Product

 

공통의 프로퍼티와 메서드를 가지는 인터페이스 객체

팩토리의 팩토리 메서드를 통해 생성되는 모든 객체들에 대한 공통 인터페이스이다.

 

[2] Concrete Product

 

이름의 콘크리트처럼, Product를 구현한 최종의 객체를 의미한다.

Product를 준수한다면 해당 Concrete Product는 무한히 생성될 수 있다.

실제 소스코드 변경에서도 Concrete Product와 Factory의 변경만으로 수정이 가능하다.

 

[3] Creator

 

위 Product를 준수하는 객체를 생성하는 메소드를 가지고 있는 인터페이스이다.

 

[4] Factory

 

실제 접근하여 객체를 생성하도록 Creator를 준수하는 객체이다.

 

 

다른 곳에서도 학습했지만,

팩토리 메서드를 이해하는 데는 정의보다 예제를 보면서

직접 자기가 생각하는 프로덕트 객체를 만들어보면서 한 번만 구현해도 쪼금 감이 온다.

나는 "치킨" 이라는 Product를 만들어서 이를 팩토리 객체를 통해 생성하는 것 까지 진행하였다.

 

/*
 팩토리 메서드 패턴
 - 정의 : 객체 생성을 서브클래스가 하도록 하는 패턴
 1. Product : 생성되는 객체에 대한 인터페이스 (공통 기능)
 2. Concrete Product : Product를 구현한 객체
 3. Creator : 새로운 객체를 반환하는 팩토리 메서드를 선언, 여기서 반환되는 객체는 반드시 Product를 준수해야한다.
 4. Factory : Creator를 구현한 실제 객체를 생성하는 클래스
 */


/*
 Product : 생성되는 객체에 대한 인터페이스 (공통 기능)
 공통 기능이므로, 공통 기능에 대한 수정만 이곳에서 일어난다.
 예 : 치킨
 - 필요 공통 기능
 1. 치킨 이름 : String
 2. 준비물 : [String]
 3. 튀김 횟수 : Int
 4. 튀김 온도들 : [Int]
 5. 치킨 가격 : Int
 6. prepare : 치킨을 튀기기전의 준비 과정
 7. cook : 치킨을 튀기는 레시피
 */

protocol Chicken {
    var name: String { get }
    var preparations: [String] { get }
    var countOfFries: Int { get }
    var fryingTemperatures: [Int] { get }
    var price: Int { get }

    func prepare()
    func cook()
}

extension Chicken {
    func prepare() {
        print("\(self): \(#function)")
        print("준비물")
        preparations.forEach { print($0) }
    }

    func cook() {
        print("\(self): \(#function)")
        print("\(countOfFries)번 튀긴다.")
        for (index, temperature) in fryingTemperatures.enumerated() {
            print("\(index + 1)번째는 \(temperature)온도로 튀긴다.")
        }
    }
}

/*
 Concrete Product : 공통 기능을 제공하는 Product 인터페이스를 구현한 실제 객체
 해당 객체는 무한히 정의할 수 있다. 새로운 구조체가 생기는 거니까 수정이 있어야한다.
 */

struct OriginalChicken: Chicken {
    var name: String {
        "\(self)"
    }
    var preparations: [String] {
        ["Salt", "Chicken"]
    }
    var countOfFries: Int {
        2
    }
    var fryingTemperatures: [Int] {
        [160, 180]
    }
    var price: Int {
        15000
    }
}

struct SpicyChicken: Chicken {
    var name: String {
        "\(self)"
    }
    var preparations: [String] {
        ["Salt", "Chicken", "Pepper"]
    }
    var countOfFries: Int {
        1
    }
    var fryingTemperatures: [Int] {
        [180]
    }
    var price: Int {
        18000
    }
}

/*
 Creator : Product를 준수하는 객체를 생성하는 팩토리 메서드를 정의한 인터페이스
 */

protocol ChickenCreator {
    func getChicken(type: ChickenType) -> Chicken
}

enum ChickenType {
    case originalChicken
    case spicyChicken
}

/*
 Factory : Creator를 준수하는 실제 Product를 생성하는 실 객체를 만드는 객체
 새로운 Chicken을 준수하는 Concrete Product가 생성되면 Creator를 구현한 하단의 팩토리 객체에서만 수정하면 생성이 가능하다.
 */

struct ChickenFactory: ChickenCreator {
    func getChicken(type: ChickenType) -> Chicken {
        switch type {
        case .originalChicken:
            return OriginalChicken()
        case .spicyChicken:
            return SpicyChicken()
        }
    }
}

let chickenFactory = ChickenFactory()

let originalChicken = chickenFactory.getChicken(type: .originalChicken)
let spicyChicken = chickenFactory.getChicken(type: .spicyChicken)

originalChicken.prepare()
originalChicken.cook()

spicyChicken.prepare()
spicyChicken.cook()

 

결과--

 

OriginalChicken(): prepare()

준비물

Salt

Chicken

OriginalChicken(): cook()

2 튀긴다.

1번째는 160온도로 튀긴다.

2번째는 180온도로 튀긴다.

SpicyChicken(): prepare()

준비물

Salt

Chicken

Pepper

SpicyChicken(): cook()

1 튀긴다.

1번째는 180온도로 튀긴다.