추상 팩토리 패턴은 생성 패턴의 한 종류로 객체의 집합을 생성할 때 유리한 패턴이다. 추상 팩토리 패턴은 앞 페이지에서 다룬 팩토리 메서드 패턴과 혼동할 수 있지만, 다른 패턴이다. 추상 팩토리 패턴은 기존에 팩토리를 한번 더 추상화 하여 서로 관련이 있는 제품군을 생성 하게 해준다.
- 추상 팩토리 패턴은 인스턴스를 생성하는 과정을 캡슐화 한 것이다. 따라서 생성에 관한 구체적인 내용이 사용자와 분리된다.
- 구체 팩토리를 추가하거나 변경을 통해 서로 다른 제품을 사용할 수 있게 할 수 있다. 추상 팩토리는 필요한 모든 것을 생성하기 때문에 전체 제품군은 한 번에 변경이 가능하다.
- 사용자(Factory를 사용하는 타입인 ContentUI) 입장에서는 분기 처리를 위한 ContentType을 이용하여 처리한다.
- 사용자는 모든 UI 요소들의 Factory를 가지고 있어야 한다.
- 결국 부품이 많아지면(Button, Label 같은 요소들이 계속 추가 된다면) 사용자 입장에서 관리하기 까다로워 진다.
- 여기서 만약 새로운 기기 Apple Watch가 추가 된다면 어떻게 될까? 먼저 모든 Factor에 Apple Watch에 관한 분기 처리를 추가해줘야 한다. 그리고 Factory 쪽 코드 뿐만 아니라 사용자도 Apple Watch를 생성하는 경우 내부 코드 변경이 필요하다.
- 따라서 연관이 있는 여러 객체를 묶어서 생성하는 경우에 Abstract Factory 패턴이 Factory 패턴에 비해 유리할 수 있다.
- UIFactoryable: Factory 추상화 타입
- 이 인터페이스는 제품을 생성하는 데 필요한 메서드들을 선언한다. 일반적으로 이 인터페이스는 여러 종류의 제품을 생성하는 메서드들로 이루어져 있다.
- IPhoneFactory, IPadFactory: 각 연관이 있는 인스턴스 집합을 생성할 구체 Factory 타입
- AbstractFactory 인터페이스를 구현하는 클래스들이다. 이 클래스들은 실제 제품 객체를 생성하는 작업을 수행한다. 각 Concrete Factory는 특정 제품군을 만드는 데 특화되어 있다.
- Buttonable, Labelable: 생성되는 인스터스를 추상화한 타입
- 제품군을 속하는 개별 제품들의 공통 특성을 정의하는 인터페이스이다.
- IPhoneButton, IPadLabel...: 최종적으로 생성되는 구체적인 타입
- AbstractProduct 인터페이스를 사용하여 필요한 제품을 생성하는 클래스이다. Client는 ConcreteFactory나 ConcreteProduct 클래스를 직접 참조하지 않고, 대신 AbstractFactory를 통해 제품을 생성하므로 시스템이 유연해진다.
import Foundation
protocol Animal {
func name() -> String
}
protocol Sound {
func noise() -> String
}
class Dog: Animal {
func name() -> String {
return "Dog"
}
}
class Cat: Animal {
func name() -> String {
return "Cat"
}
}
class Bark: Sound {
func noise() -> String {
return "Bark"
}
}
class Meow: Sound {
func noise() -> String {
return "Meow"
}
}
protocol AnimalFactory {
func createAnimal() -> Animal
func createSound() -> Sound
}
class DogFactory: AnimalFactory {
func createAnimal() -> Animal {
return Dog()
}
func createSound() -> Sound {
return Bark()
}
}
class CatFactory: AnimalFactory {
func createAnimal() -> Animal {
return Cat()
}
func createSound() -> Sound {
return Meow()
}
}
let dogFactory = DogFactory()
let dog = dogFactory.createAnimal()
let bark = dogFactory.createSound()
let catFactory = CatFactory()
let cat = catFactory.createAnimal()
let meow = catFactory.createSound()
// 추상화된 Factory
protocol UIFactoryable {
func createButton() -> Buttonable
func createLabel() -> Labelable
}
// 연관된 제품군을 실제로 생성하는 구체 Factory
final class IPadUIFactory: UIFactoryable {
func createButton() -> Buttonable {
return IPadButton()
}
func createLabel() -> Labelable {
return IPadLabel()
}
}
final class IPhoneUIFactory: UIFactoryable {
func createButton() -> Buttonable {
return IPhoneButton()
}
func createLabel() -> Labelable {
return IPhoneLabel()
}
}
// 추상화된 Product
protocol Buttonable {
func touchUp()
}
protocol Labelable {
var title: String { get }
}
// 실제로 생성될 구체 Product, 객체가 가질 기능과 상태를 구현
final class IPhoneButton: Buttonable {
func touchUp() {
print("IPhone Button")
}
}
final class IPadButton: Buttonable {
func touchUp() {
print("IPad Button")
}
}
final class IPhoneLabel: Labelable {
var title: String = "iPhoneLabel"
}
final class IPadLabel: Labelable {
var title: String = "iPadLabel"
}
class ViewController: UIViewController {
//UI를 가지고 있는 인스턴스 기기별로 설정
var iPadUIContent = UIContent(uiFactory: IPadUIFactory())
var iPhoneUIContent = UIContent()
override func viewDidLoad() {
super.viewDidLoad()
touchUpButton()
printLabelTitle()
}
func touchUpButton() {
iPadUIContent.button?.touchUp()
iPhoneUIContent.button?.touchUp()
}
func printLabelTitle() {
print(iPadUIContent.label?.title ?? "")
print(iPhoneUIContent.label?.title ?? "")
}
}
// Factory를 통해 UI를 만들고 가지고 있는 Class
class UIContent {
var uiFactory: UIFactoryable
var label: Labelable?
var button: Buttonable?
init(uiFactory: UIFactoryable = IPhoneUIFactory()) {
self.uiFactory = uiFactory
setUpUI()
}
func setUpUI() {
label = uiFactory.createLabel()
button = uiFactory.createButton()
}
}
// 추상 팩토리
protocol ThemeFactory {
func createBackgroundColor() -> UIColor
func createTextColor() -> UIColor
}
// 라이트 테마 팩토리
class LightThemeFactory: ThemeFactory {
func createBackgroundColor() -> UIColor {
return UIColor.white
}
func createTextColor() -> UIColor {
return UIColor.black
}
}
// 다크 테마 팩토리
class DarkThemeFactory: ThemeFactory {
func createBackgroundColor() -> UIColor {
return UIColor.black
}
func createTextColor() -> UIColor {
return UIColor.white
}
}
// ViewController
class ViewController: UIViewController {
private var themeFactory: ThemeFactory!
// viewController 초기화 시 테마 팩토리 전달
init(themeFactory: ThemeFactory) {
super.init(nibName: nil, bundle: nil)
self.themeFactory = themeFactory
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = themeFactory.createBackgroundColor()
let label = UILabel()
label.textColor = themeFactory.createTextColor()
self.view.addSubView(label)
}
}
Network
와 Database
에서 데이터를 가져오는 경우를 보여진다. DataSourceFactory
라는 프로토콜을 정의하고, createProductListDataSource()
와 createProductDetailSource()
두 가지 메소드를 추가한다. protocol DataSourceFactory {
func createProductListDataSource() -> ProductListDataSource
func createProductDetailDataSource() -> ProductDetailDataSource
}
protocol ProductListDataSource {
func fetchProductList(completion: @escaping ([Product])-> Void)
}
protocol ProductDetailDataSource {
func fetchProductDetail(for productId: String, completion: @escaping (ProductDetail) -> Void)
}
// 네트워크와 데이터베이스에 대한 구체적인 팩토리
class NetworkDataSourceFactory: DataSourceFactory {
func createProductListDataSource() -> ProductListDataSource {
return NetworkProductListDataSource()
}
func createProductDetailDataSource() -> ProductDetailDataSource {
return NetworkProductDetailDataSource()
}
}
class NetworkProductListDataSource: ProductListDataSource {
func fetchProductList(completion: @escaping ([Product]) -> Void) {
// Network request and parse data
}
}
class NetworkProductDetailDataSource: ProductDetailDataSource {
func fetchProductDetail(for productId: String, completion: @escaping (ProductDetail) -> Void) {
// Network request and parse data
}
}
class DatabaseDataSourceFactory: DataSourceFactory {
func createProductListDataSource() -> ProductListDataSource {
return DatabaseProductListDataSource()
}
func createProductDetailDataSource() -> ProductDetailDataSource {
return DatabaseProductDetailDataSource()
}
}
class DatabaseProductListDataSource: ProductListDataSource {
func fetchProductList(completion: @escaping ([Product]) -> Void) {
// Fetch data from database
}
}
class DatabaseProductDetailDataSource: ProductDetailDataSource {
func fetchProductDetail(for productId: String, completion: @escaping (ProductDetail) -> Void) {
// Fetch data from database
}
}
class ViewController: UIViewController {
private var dataSourceFactory: DataSourceFactory!
// ViewController 초기화 시 DataSourceFactory 전달
init(dataSourceFactory: DataSourceFactory) {
super.init(nibName: nil, bundle: nil)
self.dataSourceFactory = dataSourceFactory
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
let productListDataSource = dataSourceFactory.createProductListDataSource()
productListDataSource.fetchProductList { productList in
// Update UI
}
let productDetailDataSource = dataSourceFactory.createProductDetailDataSource()
productDetailDataSource.fetchProductDetail(for: "productId") { productDetail in
// Update UI
}
}
}
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다.