지난 Singleton 패턴에 이어 이번에는 GoF 패턴 중 생성에 속해있는 Factory Method에 대해 작성해 보겠습니다.
구체적으로 어떤 인스턴스를 만들지는 서브 클래스가 결정하는 패턴을 말합니다.

ConcreteProduct에서 생성한다.Creator 프로토콜을 채택한 타입에서는 각각 다른 타입을 반환합니다.Product 같은 경우 프로토콜을 이용해 구현할 수 있지만 제가 생각했을 때 좀 더 구체적인 타입에서 인스턴스를 생성한다는 말이 상속이 어울리는 것 같아 UML을 위에 이미지처럼 작성했습니다.
하나의 Factory에서 모든 자동차를 만드는 코드를 작성해 보겠습니다.
패턴 적용 전
class Car {
var name: String
var color: String
init(name: String, color: String) {
self.name = name
self.color = color
}
}
class CarFactory {
func makeCar(name: String, color: String) -> Car? {
if name == "k3" {
return Car(name: name, color: color)
} else if name == "K5" {
return Car(name: name, color: color)
} else if name == "K7" {
return Car(name: name, color: color)
}
return nil
}
}
위 코드를 보면 CarFactory에서 makeCar 메서드를 이용해 자동차를 만들고 있습니다.
위 코드의 문제점
1. makeCar 메서드 내부에서 분기 처리를 통해 각각 다른 인스턴스를 생성해야 한다.
2. 특별한 프로퍼티를 갖는 객체를 만들고 싶을 때 기존에 구현한 Car를 수정해야 한다. 그렇다면 의도하지 않은 변경이 발생할 수 있고 기존의 코드를 변경해야 한다.
기존의 코드를 변경하게 되면 객체지향 원칙 중 OCP를 위반하게 된다
(Car를 확장하기 위해서는 계속해서 기존 코드를 변경해야 한다.)
OCP?
확장에는 열려 있어야 하지만 변경에는 닫혀 있어야 하는 객체 지향 다섯 가지 원칙 중 하나이다.
패턴 적용
protocol Factory {
func orederCar(color: String) -> Car
func makeCar(color: String) -> Car
}
extension Factory {
func orederCar(color: String) -> Car {
prepare()
let car = makeCar(color: color)
success()
return car
}
private func prepare() {
print("차를 준비중입니다.")
}
private func success() {
print("완성했습니다.")
}
}
orederCar 메서드 같은 경우 프로토콜을 확장해 기본 구현을 해주었다.
class K3Factory: Factory {
func makeCar(color: String) -> Car {
return K3(name: "K3", color: color)
}
}
class CarnivalFactory: Factory {
func makeCar(color: String) -> Car {
return Carnival(option: "추가", color: color)
}
}
각 Factory에서 다른 인스턴스를 생성해 반환한다.
class Car {
var name: String
var color: String
init(name: String, color: String) {
self.name = name
self.color = color
}
}
class K3: Car { }
class Carnival: Car {
let option: String
init(option: String, color: String) {
self.option = option
super.init(name: "Carnival", color: color)
}
}
let myCar = K3Factory().orederCar(color: "White")
let secondCar = CarnivalFactory().orederCar(color: "black") as! Carnival
타입 캐스팅 을 통해 구체적인 타입으로 변환하고 해당 타입에 있는 메소드를 사용하거나 프로퍼티에 접근할 수 있습니다.
이제 Factory Method의 장점과 단점을 알아보겠습니다.
🤔 프로토콜에 의존하기 때문에 관계를 느슨하게 유지하는 걸까?
상속을 사용 하더라도 상위 클래스에 의존하기 때문에 관계를 느슨하게 유지 할 수 있는지가 궁금합니다...... (아시는 분은 댓글로 남겨주세요 😭)
Creator를 알고 있어야 합니다.해당 글은 인프런의 코딩으로 학습하는 GoF 디자인 패턴 강의와 블로그를 참고해 작성했습니다.