추상 팩토리
- 생성패턴
- 상세화된 서브클래스 정의없이 서로 관련성이 있거나 독립적인 객체의 군을 생성하기 위한 디자인 패턴
단일 책임 원칙 - 제품 생성 코드를 한 곳으로 추출하여 코드를 더 쉽게 유지보수
개방/폐쇄 원칙 - 기존 클라이언트 코드를 훼손하지 않고 제품의 새로운 변형들을 생성
활용성
- 객체가 생성되거나 구성, 표현되는 방식과 무관하게 시스템을 독립적으로 만들 때
- 여러개 중 하나를 선택해서 객체를 만들고, 그 객체를 다른 것으로 대체할 때
- 관련 객체들이 함께 사용되도록 하고, 제약사항을 외부에서 지키도록 하고 싶을 때
- 객체에 대한 클래스 라이브러리를 제공하고, 구현이 아닌 인터페이스를 노출하고 싶을 때
구조
요소
- 추상 팩토리
- 실제로 만들어야 할 객체에 대한 인터페이스 정의
- 구체화 된 팩토리
- 추상 팩토리의 실체화
- 구체적인 객체를 생성하는 연산 구현
- 추상 객체
- 구체화 된 객체
- 추상 객체의 실체화
- 구체적으로 팩토리가 생성할 객체를 정의
- 클라이언트
- 추상팩토리, 추상객체에 선언된 인터페이스 사용
장점
- 구체적인 객체 분리
- 제품 생성 코드를 한 곳으로 추출하여 코드를 더 쉽게 유지보수할 수 있다.
- 기존 클라이언트 코드를 훼손하지 않고 제품의 새로운 변형들을 생성할 수 있다.
단점
- 패턴과 함께 새로운 인터페이스들과 클래스들이 많이 도입되기 때문에 코드가 필요 이상으로 복잡해질 수 있다.
- 새로운 종류의 객체를 제공하기 어렵다.
- 기존 추상 팩토리를 확장하기 어려움. 팩토리의 구현 변경 시 수정해야 하는 구현 객체들이 모두 변경되야 한다.
예시 코드
protocol Vehicle {
var name: String { get }
var id: String { get }
var wheel: Int { get }
}
struct Car: Vehicle {
var name: String = "차"
var id: String = "1"
var wheel: Int = 4
}
struct Scooter: Vehicle {
var name: String = "스쿠터"
var id: String = "2"
var wheel: Int = 2
}
protocol FactoryProtocol {
func createVehicle() -> Vehicle
}
struct ScooterFactory: FactoryProtocol {
func createVehicle() -> Vehicle {
return Scooter()
}
}
struct CarFactory: FactoryProtocol {
func createVehicle() -> Vehicle {
return Car()
}
}
struct Client {
let factory: FactoryProtocol
init(factory: FactoryProtocol) {
self.factory = factory
}
}
let client1 = Client(factory: CarFactory())
let car = client1.factory.createVehicle()
let client2 = Client(factory: ScooterFactory())
let scooter = client2.factory.createVehicle()
내 생각
- 객체 자체와 객체를 만들 수 있는 팩토리 모두 추상화를 통해 실제 클라이언트는 팩토리에만 의존하고 있고 어떤 객체를 만들지에 대해 의존성 주입을 해주기 때문에 실제 사용한하는 객체의 변경이 있더라도 클라이언트엔 영향이 가지 않을 것 같다.
- 공통점을 가진 여러가지 객체를 사용할 때 좋을 듯 하다. Repository 패턴도 이와 비슷하지 않을까..?
참고
associatedType이 있는 protocol의 경우에 제네릭으로 해결할 수 있지만,
some 키워드를 통해 추상화된 형태로 내보낼 수 있다는 지점에서...
some 키워드가 추상 팩토리를 구현하는데 도움이 되기도 하겠네요 :)