시스템이 사용할 구체적인 클래스 타입에 대한 결정을 서브 클래스로 위임하는 패턴이다.

 

1️⃣ 동기

프레임워크를 개발하는 경우 클라이언트가 원하는 구체적인 동작사항을 프레임워크 제공자가 모두 예측하여 구현할 수 없습니다.

이 경우 시스템이 활용하는 인터페이스를 클라이언트에게 제공하고 클라이언트가 인터페이스를 구체적인 객체를 구현한 후 시스템에게 전달하는 구조가 필요합니다.

팩토리 매서드 패턴은 어떤 구체적인 객체를 생성해야 하는지에 대한 정보를 캡슐화함으로써 그것을 프레임워크로 부터 분리합니다.

 

2️⃣ 활용성

  • 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때
    • 추상화를 통해 확정성을 확보합니다.
  • 생성에 대한 책임을 서브 클래스로 위임하고 싶을 때
    • 여기서 말하는 서브클래스는 추상 팩토리 매서드를 오버라이딩하여 구체 객체를 반환하는 클래스를 의미합니다.

 

3️⃣ 패턴 참여자

  • 프로덕트: 팩토리 매서드가 반환하는 객체 인터페이스입니다.
  • 구체 프로덕트: 프로덕트 인터페이스를 구현한 구체 객체입니다.
  • 크리에이터:  프로덕트 객체를 생성하는 팩토리 매서드를 선언합니다.
    • 서브 클래스를 사용하지 않는 경우를 위해 기본값을 반환하기도 합니다.
  • 구체 크리에이터: 팩토리 매서드를 재정의 하여 구체 프로덕트를 반환합니다.

 

4️⃣ 결과

  • 특정 프로그램에 국한된 구체적인 객체가 코드를 사용하는 곳에 종속적이지 않게됩니다.

 

5️⃣ Swift구현

구현 방법은 크게 두가지입니다.

  1. 추상 클래스를 정의하고, 정의한 팩토리 매서드에 대한 기본 구현을 제공하지 않는 경우
  2. 크리에이터 자체가 구체 클래스로 동작하며 기본 구현을 제공하는 경우

로봇의 팔을 만들어내는 크리에이터를 만듭니다.

기본 구현을 제공하지 않는 방식을 사용해보겠습니다.

protocol RobotArm {
    func shake()
}

class RobotArmCreator {
    final func activate() {
        let arm = createArm()
        arm.shake()
    }
    
    func createArm() -> RobotArm { fatalError() }
}

 

구체적인 로봇 팔을 생성하는 구체 크리에이터를 만듭니다.

struct GreenArm: RobotArm {
    func shake() { print("Green shake") }
}

final class GreenRobotArmCreator: RobotArmCreator {
    override func createArm() -> any RobotArm {
        GreenArm()
    }
}

struct GrayArm: RobotArm {
    func shake() { print("Gray shake") }
}

final class GrayRobotArmCreator: RobotArmCreator {
    override func createArm() -> any RobotArm {
        GrayArm()
    }
}

 

각각의 크리에이터를 사용합니다.

let systemObject1: RobotArmCreator = GreenRobotArmCreator()
systemObject1.activate() // Green shake

let systemObject2: RobotArmCreator = GrayRobotArmCreator()
systemObject2.activate() // Gray shake

 

매개변수화된 팩토리 매서드

아래 방식은 구체적인 객체를 식별할 수 있는 식별자를 팩토리 매서드에 전달하여 분기를 통해 특정 객체를 생성하는 방식입니다.

새로운 타입을 추가하는 경우 기존의 분기문을 수정하지 않고 상위 클래스의 함수를 연쇄적으로 호출하는 방법을 사용할 수 있습니다.

분기 문을 사용한다는 점에서 새로운 항목이 추가될 때마다 코드를 수정해야함으로 OCP에 위배되는 접근법입니다.

따라서 항목의 수가 적고 변경이 적은 경우를 제외하고는 지양하는 것이 좋을 것 같은 접근법입니다.

class RobotArmCreator2 {
    func createArm(id: String) -> RobotArm {
        switch id {
        case "green":
            GreenArm()
        case "gray":
            GrayArm()
        default:
            fatalError()
        }
    }
}

class SubRobotArmCreator2: RobotArmCreator2 {
    override func createArm(id: String) -> any RobotArm {
        switch id {
        case "yellow":
            YellowArm()
        default:
            super.createArm(id: id)
        }
    }
}

 

 

6️⃣ 느낀점

프레임워크의 협력 시스템을 유지하면서 클라이언트의 다양성을 부과한다는 점이 인상적이다.

추후 프레임워크나 라이브러리를 개발하는 경우 해당 패턴을 활용하여 클라이언트의 확장성을 확보해야 겠다.

추상 팩토리 패턴과의 차이점

추상 팩토리 패턴은, 관련된 구체 객체들의 집합(Kit)을 생성하는 팩토리를 다룬다는 점에서, 보다 포괄적이고 큰 개념이라 볼 수 있습니다.

반면, 팩토리 메서드 패턴은 특정 동작에 참여할 구체 객체의 생성을 하위 클래스에 위임한다는 점에 초점을 둔 패턴입니다.