Abstract Factory 패턴의 실제 사용 예제에 대해 알아봅시다.
아래 개념 설명 글을 보고 오시면 더 쉽게 이해할 수 있습니다.
https://littlemoom.tistory.com/58
[GoF Design Patterns] Abstract Factory
* 예제 코드 및 설명에 필요한 개념들이 Swift를 기준으로 작성된 글입니다. 지난번의 Factory Method에 이어서 오늘은 Abstract Factory 패턴에 대해 알아보겠습니다.Abstract Factory 또한 Creational Patterns 중
littlemoom.tistory.com
* 편의를 위하여 예제의 Input Type을 String
으로 통일하였습니다.
* 정의되는 개념이 많아 개념 구조 이미지에서의 명칭을 함께 표기하였습니다. 참고하여 보시면 이해하기 쉽습니다.
* Reference 코드를 재구성하여 작성된 예제입니다.
https://refactoring.guru/ko/design-patterns/abstract-factory/swift/example#example-1
스위프트로 작성된 추상 팩토리 / 디자인 패턴들
사용 예시들: 추상 팩토리 패턴은 스위프트 코드에 자주 사용됩니다. 많은 프레임워크들과 라이브러리들은 이 패턴을 표준 컴포넌트들을 확장 및 사용자 지정하기 위해 사용합니다. 식별: 패턴
refactoring.guru
설계 배경
e커머스 앱에서 제품을 구매하는데 있어 주소 입력과 결제 방법 선택이 필요하고, 회원과 비회원 고객이 있다고 가정해봅시다.
여기서 Abstract Product의 종류는 주소 입력 화면과 결제 방법 선택 화면이 되고,
Abstract Factory의 종류는 회원과 비회원이 됩니다.
Product 정의
해당 상황에서 Abstract Product의 종류는 주소 입력과 결제 방법 입력입니다.
먼저 주소 입력 프로토콜(Abstract ProductA
)을 정의하고,
회원 주소 입력 화면(Concrete ProductA1
)과 비회원 주소 입력 화면(Concrete ProductA2
)을 정의합니다.
UI Component의 구성은 거의 동일할 수 있습니다.
// ****************
// * Addres *
// ****************
protocol Address {
func inputEmail(to: String)
func inputAddress(to: String)
func inputZipNumber(to: String)
var contentView: AddressView { get }
}
// 공통 AddressView
class AddressView: UIView {
public let emailField = UITextField()
public let addressField = UITextField()
public let zipNumberField = UITextField()
public let nextButton = UIButton()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 회원 AddressView
class MemberAddressView: UIView, Address {
func inputEmail(to: String) { }
func inputAddress(to: String) { }
func inputZipNumber(to: String) { }
var contentView: AddressView {
AddressView()
}
}
// 비회원 AddressView
class NonMemberAddressView: UIView, Address {
func inputEmail(to: String) { }
func inputAddress(to: String) { }
func inputZipNumber(to: String) { }
// 비회원의 경우 별도의 정보를 입력하는 UI가 필요하다.
var contentView: AddressView {
let view = AddressView()
let nameField = UITextField()
let phoneNumberField = UITextField()
view.addSubview(nameField)
view.addSubview(phoneNumberField)
return view
}
override var description: String {
return "Non-Member-Address-View"
}
}
같은 방법으로 결제 방법 입력 프로토콜(Abstract ProductB
)을 정의하고,
회원 결제 방법 입력 화면(Concrete ProductB1
)과 비회원 결제 방법 입력 화면(Concrete ProductB2
)을 정의합니다.
// ************************
// * PaymentMethod *
// ************************
protocol PaymentMethod {
func selectPaymentMethod(type: String)
func inputPaymentInfo(info: String)
func setInstallmentMonth(mm: String)
var contentView: PaymentMethodView { get }
}
// 공통 PaymentMethodView
class PaymentMethodView: UIView {
public let cardNumberField = UITextField()
public let installmentMonthField = UITextField()
public let checkVaildButton = UIButton()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 회원 PaymentMethodView
class MemberPaymentMethodView: UIView, PaymentMethod {
func selectPaymentMethod(type: String) { }
func inputPaymentInfo(info: String) { }
func setInstallmentMonth(mm: String) { }
var contentView: PaymentMethodView {
PaymentMethodView()
}
}
// 비회원 PaymentMethodView
class NonMemberPaymentMethodView: UIView, PaymentMethod {
func selectPaymentMethod(type: String) { }
func inputPaymentInfo(info: String) { }
func setInstallmentMonth(mm: String) { }
var contentView: PaymentMethodView {
PaymentMethodView()
}
}
Factory 정의
이제 Abstract Factory에 대해 정의해봅시다.
Abstract Factory는 회원의 타입 값을 가지고 있고, 주소 혹은 결제 방법 입력 화면을 생성합니다.
protocol OrderViewFactory {
func OrderAddressViewController() -> AddressViewController
func OrderPaymentMethodViewController() -> PaymentMethodViewController
}
class AddressViewController: UIViewController {
fileprivate var contentView: Address
init(contentView: Address) {
self.contentView = contentView
super.init(nibName: nil, bundle: nil)
}
required convenience init?(coder aDecoder: NSCoder) {
return nil
}
}
class PaymentMethodViewController: UIViewController {
fileprivate var contentView: PaymentMethod
init(contentView: PaymentMethod) {
self.contentView = contentView
super.init(nibName: nil, bundle: nil)
}
required convenience init?(coder aDecoder: NSCoder) {
return nil
}
}
이제 회원 주문 화면 생성 객체(Concrete Factory1
)와 비회원 주문 화면 생성 객체(Concrete Factory2
)를 정의해봅시다.
class MemberOrderViewFactory: OrderViewFactory {
func OrderAddressViewController() -> AddressViewController {
let addressView = MemberAddressView()
return AddressViewController(contentView: addressView)
}
func OrderPaymentMethodViewController() -> PaymentMethodViewController {
let paymentMethodView = MemberPaymentMethodView()
return PaymentMethodViewController(contentView: paymentMethodView)
}
}
class NonMemberOrderViewFactory: OrderViewFactory {
func OrderAddressViewController() -> AddressViewController {
let addressView = NonMemberAddressView()
return AddressViewController(contentView: addressView)
}
func OrderPaymentMethodViewController() -> PaymentMethodViewController {
let paymentMethodView = NonMemberPaymentMethodView()
return PaymentMethodViewController(contentView: paymentMethodView)
}
}
Client 코드
이제 클라이언트 코드에서 어떻게 사용되는지 살펴봅시다.
enum MemberType {
case member
case non_member
}
class Client {
private let memberType: MemberType
private var currentOrderStep: OrderStep?
private var factory: OrderViewFactory {
switch memberType {
case .member: return MemberOrderViewFactory()
case .non_member: return NonMemberOrderViewFactory()
}
}
init(memberType: MemberType) {
self.memberType = memberType
}
/// MARK: - Presentation
func presentInputAddressView() {
currentOrderStep = .address
let controller = factory.OrderAddressViewController()
frontViewController.pushViewController(controller, animated: true)
}
func presentInputPaymentMethodView() {
currentOrderStep = .paymentMethod
let controller = factory.OrderPaymentMethodViewController()
frontViewController.pushViewController(controller, animated: true)
}
}
let client = Client(memberType: .member)
client.presentInputAddressView()
MemberType
에 맞는 Factory를 생성하여 화면을 표시할 수 있습니다.
예제 Test 코드
class AbstractFactoryRealWorld: XCTestCase {
func testFactoryMethodRealWorld() {
var memberType: MemberType = .non_member
if let _ = userInfo {
memberType = .member
}
let client = Client(memberType: memberType)
/// Present Input Address Flow
client.presentInputAddressView()
print("Address screen has been presented")
/// Present Input Payment Method Flow
client.presentInputPaymentMethodView()
print("Payment Method screen has been presented")
}
}
이렇게 활용하여 UI Test 코드도 작성해볼 수 있겠습니다.
전체 코드
// ****************
// * Addres *
// ****************
protocol Address {
func inputEmail(to: String)
func inputAddress(to: String)
func inputZipNumber(to: String)
var contentView: AddressView { get }
}
// 공통 AddressView
class AddressView: UIView {
public let emailField = UITextField()
public let addressField = UITextField()
public let zipNumberField = UITextField()
public let nextButton = UIButton()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 회원 AddressView
class MemberAddressView: UIView, Address {
func inputEmail(to: String) { }
func inputAddress(to: String) { }
func inputZipNumber(to: String) { }
var contentView: AddressView {
AddressView()
}
}
// 비회원 AddressView
class NonMemberAddressView: UIView, Address {
func inputEmail(to: String) { }
func inputAddress(to: String) { }
func inputZipNumber(to: String) { }
// 비회원의 경우 별도의 정보를 입력하는 UI가 필요하다.
var contentView: AddressView {
let view = AddressView()
let nameField = UITextField()
let phoneNumberField = UITextField()
view.addSubview(nameField)
view.addSubview(phoneNumberField)
return view
}
override var description: String {
return "Non-Member-Address-View"
}
}
// ************************
// * PaymentMethod *
// ************************
protocol PaymentMethod {
func selectPaymentMethod(type: String)
func inputPaymentInfo(info: String)
func setInstallmentMonth(mm: String)
var contentView: PaymentMethodView { get }
}
// 공통 PaymentMethodView
class PaymentMethodView: UIView {
public let cardNumberField = UITextField()
public let installmentMonthField = UITextField()
public let checkVaildButton = UIButton()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 회원 PaymentMethodView
class MemberPaymentMethodView: UIView, PaymentMethod {
func selectPaymentMethod(type: String) { }
func inputPaymentInfo(info: String) { }
func setInstallmentMonth(mm: String) { }
var contentView: PaymentMethodView {
PaymentMethodView()
}
}
// 비회원 PaymentMethodView
class NonMemberPaymentMethodView: UIView, PaymentMethod {
func selectPaymentMethod(type: String) { }
func inputPaymentInfo(info: String) { }
func setInstallmentMonth(mm: String) { }
var contentView: PaymentMethodView {
PaymentMethodView()
}
}
// **************************
// * OrderViewFactory *
// **************************
protocol OrderViewFactory {
func OrderAddressViewController() -> AddressViewController
func OrderPaymentMethodViewController() -> PaymentMethodViewController
}
class AddressViewController: UIViewController {
fileprivate var contentView: Address
init(contentView: Address) {
self.contentView = contentView
super.init(nibName: nil, bundle: nil)
}
required convenience init?(coder aDecoder: NSCoder) {
return nil
}
}
class PaymentMethodViewController: UIViewController {
fileprivate var contentView: PaymentMethod
init(contentView: PaymentMethod) {
self.contentView = contentView
super.init(nibName: nil, bundle: nil)
}
required convenience init?(coder aDecoder: NSCoder) {
return nil
}
}
class MemberOrderViewFactory: OrderViewFactory {
func OrderAddressViewController() -> AddressViewController {
let addressView = MemberAddressView()
return AddressViewController(contentView: addressView)
}
func OrderPaymentMethodViewController() -> PaymentMethodViewController {
let paymentMethodView = MemberPaymentMethodView()
return PaymentMethodViewController(contentView: paymentMethodView)
}
}
class NonMemberOrderViewFactory: OrderViewFactory {
func OrderAddressViewController() -> AddressViewController {
let addressView = NonMemberAddressView()
return AddressViewController(contentView: addressView)
}
func OrderPaymentMethodViewController() -> PaymentMethodViewController {
let paymentMethodView = NonMemberPaymentMethodView()
return PaymentMethodViewController(contentView: paymentMethodView)
}
}
// ****************
// * Client *
// ****************
enum MemberType {
case member
case non_member
}
class Client {
private let memberType: MemberType
private var currentOrderStep: OrderStep?
private var factory: OrderViewFactory {
switch memberType {
case .member: return MemberOrderViewFactory()
case .non_member: return NonMemberOrderViewFactory()
}
}
init(memberType: MemberType) {
self.memberType = memberType
}
/// MARK: - Presentation
func presentInputAddressView() {
currentOrderStep = .address
let controller = factory.OrderAddressViewController()
frontViewController.pushViewController(controller, animated: true)
}
func presentInputPaymentMethodView() {
currentOrderStep = .paymentMethod
let controller = factory.OrderPaymentMethodViewController()
frontViewController.pushViewController(controller, animated: true)
}
}
let client = Client(memberType: .member)
client.presentInputAddressView()
// *******************
// * Test Code *
// *******************
class AbstractFactoryRealWorld: XCTestCase {
func testFactoryMethodRealWorld() {
var memberType: MemberType = .non_member
if let _ = userInfo {
memberType = .member
}
let client = Client(memberType: memberType)
/// Present Input Address Flow
client.presentInputAddressView()
print("Address screen has been presented")
/// Present Input Payment Method Flow
client.presentInputPaymentMethodView()
print("Payment Method screen has been presented")
}
}
Improvement
레퍼런스 사이트에서는 Product는 Login
과 SignUp
, Factory는 Student
와 Teacher
로 정의했습니다.
Login
과 SignUp
을 Auth
라는 프로토콜로 묶고 var authHandler: AuthView.AuthAction?
을 통해 필요한 로직을 전달하고 있습니다.
레퍼런스 코드에서는 실제로 로직을 전달하고 있지 않아, 해당 예제에서는 별도로 코드를 추가하지는 않았습니다.
해당 예제에서 Address
와 PaymentMethod
를 Order
로 묶는다면 어떨까요?
예를 들어 회원과 비회원 모두 주소, 결제 방법 등의 정보를 입력하지만, 회원일 경우에는 기존의 정보를 가져올 수도 있습니다.
먼저 회원 정보를 담을 Member
객체와 Address
와 PaymentMethod
를 묶을 Order
프로토콜을 정의합니다.
이후 작동을 확인하기 위한 description
변수를 정의합니다.
그리고 Address
와 PaymentMethod
프로토콜은 Order
프로토콜을 상속받습니다.
struct Member {
var name: String? = "Rey"
var phone: String? = "010-1234-5678"
var email: String? = "tempEmail@domain.com"
var address: String? = "Korea Seoul"
var ZipNumber: String?
var paymentInfo: String?
}
protocol Order {
var description: String { get }
}
protocol Address: Order { ... }
protocol PaymentMethod: Order { ... }
Address
와 PaymentMethod
를 채택하는 모든 View 클래스에 description
을 override 해줍니다.
// in MemberAddressView, NonMemberAddressView,
// MemberPaymentMethodView, NonMemberPaymentMethodView
override var description: String {
return "\(Self.self)"
}
AddressView
와 PaymentMethodView
에는 각각 fetchData(memberInfo: Member)
함수를 추가해줍니다.
// in AddressView
func fetchData(memberInfo: Member) {
emailField.text = memberInfo.email
addressField.text = memberInfo.address
zipNumberField.text = memberInfo.ZipNumber
print("\(#function) in \(Self.self)")
}
// in PaymentMethodView
func fetchData(memberInfo: Member) {
cardNumberField.text = memberInfo.paymentInfo
print("\(#function) in \(Self.self)")
}
MemberAddressView
와 MemberPaymentMethodView
에 Member
데이터를 인자로 받는 초기화 함수를 정의해줍니다.
// in MemberAddressView, MemberPaymentMethodView
init(frame: CGRect = .zero, memberInfo: Member) {
super.init(frame: frame)
let view = contentView
view.fetchData(memberInfo: memberInfo)
self.addSubview(view)
}
Address
와 PaymentMethod
가 Order
로 묶였기 때문에 ViewController 또한 OrderViewController
의 상속을 받도록 수정합니다.
class OrderViewController: UIViewController {
fileprivate var contentView: Order
init(contentView: Order) {
self.contentView = contentView
print(self.contentView.description)
super.init(nibName: nil, bundle: nil)
}
required convenience init?(coder aDecoder: NSCoder) {
return nil
}
}
class MemberOrderViewController: OrderViewController {
}
class NonMemberOrderViewController: OrderViewController {
}
이제 Factory를 본격적으로 수정해봅시다.
더 이상 Factory 추상화 단계에서 각각의 ViewController를 생성할 필요가 없어졌습니다.
protocol OrderViewFactory {
func orderView(step: OrderStep) -> Order
func orderViewController(step: OrderStep) -> OrderViewController
}
Concrete Factory 내부에서 OrderStep
에 따라 View와 ViewController를 생성하여 반환합니다.
class MemberOrderViewFactory: OrderViewFactory {
var member: Member
func orderView(step: OrderStep) -> Order {
switch step {
case .address: return MemberAddressView(memberInfo: member)
case .paymentMethod: return MemberPaymentMethodView(memberInfo: member)
}
}
func orderViewController(step: OrderStep) -> OrderViewController {
var contentView = self.orderView(step: step)
return MemberOrderViewController(contentView: contentView)
}
init(member: Member) {
self.member = member
}
}
class NonMemberOrderViewFactory: OrderViewFactory {
func orderView(step: OrderStep) -> Order {
switch step {
case .address: return NonMemberAddressView()
case .paymentMethod: return NonMemberPaymentMethodView()
}
}
func orderViewController(step: OrderStep) -> OrderViewController {
let contentView = self.orderView(step: step)
return NonMemberOrderViewController(contentView: contentView)
}
}
마지막으로 클라이언트 코드는 어떻게 바뀔까요?
MemberType
을 데이터를 전달할 수 있도록 수정합니다.
MemberType
에 따라 Factory가 생성되고, 이후 OrderStep
을 통해 원하는 View를 표시할 수 있습니다.
enum MemberType {
case member(Member)
case non_member
}
class Client {
private let memberType: MemberType
private var factory: OrderViewFactory {
switch memberType {
case .member(let member): return MemberOrderViewFactory(member: member)
case .non_member: return NonMemberOrderViewFactory()
}
}
init(memberType: MemberType) {
self.memberType = memberType
}
/// MARK: - Presentation
func presentOrderView(type orderStep: OrderStep) {
let controller = factory.orderViewController(step: orderStep)
// frontViewController: UINavigationController
frontViewController.pushViewController(controller, animated: true)
}
}
따라서 클라이언트 코드는 다음과 같이 사용됩니다.
let member: Member = .init()
let memberClient = Client(memberType: .member(member))
memberClient.presentOrderView(type: .address)
//fetchData(memberInfo:) in AddressView
// MemberAddressView
let nonMemberClient = Client(memberType: .non_member)
nonMemberClient.presentOrderView(type: .paymentMethod)
// NonMemberPaymentMethodView
Improvement 전체 코드
enum OrderStep {
case address
case paymentMethod
}
struct Member {
var name: String? = "Rey"
var phone: String? = "010-1234-5678"
var email: String? = "tempEmail@domain.com"
var address: String? = "Korea Seoul"
var ZipNumber: String?
var paymentInfo: String?
}
protocol Order {
var description: String { get }
}
// ****************
// * Addres *
// ****************
protocol Address: Order {
func inputEmail(to: String)
func inputAddress(to: String)
func inputZipNumber(to: String)
var contentView: AddressView { get }
}
// 공통 AddressView
class AddressView: UIView {
public let emailField = UITextField()
public let addressField = UITextField()
public let zipNumberField = UITextField()
public let nextButton = UIButton()
func fetchData(memberInfo: Member) {
emailField.text = memberInfo.email
addressField.text = memberInfo.address
zipNumberField.text = memberInfo.ZipNumber
print("\(#function) in \(Self.self)")
}
override init(frame: CGRect = .zero) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 회원 AddressView
class MemberAddressView: UIView, Address {
func inputEmail(to: String) { }
func inputAddress(to: String) { }
func inputZipNumber(to: String) { }
var contentView: AddressView {
AddressView()
}
override var description: String {
return "\(Self.self)"
}
init(frame: CGRect = .zero, memberInfo: Member) {
super.init(frame: frame)
let view = contentView
view.fetchData(memberInfo: memberInfo)
self.addSubview(view)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 비회원 AddressView
class NonMemberAddressView: UIView, Address {
func inputEmail(to: String) { }
func inputAddress(to: String) { }
func inputZipNumber(to: String) { }
// 비회원의 경우 별도의 정보를 입력하는 UI가 필요하다.
var contentView: AddressView {
let view = AddressView()
let nameField = UITextField()
let phoneNumberField = UITextField()
view.addSubview(nameField)
view.addSubview(phoneNumberField)
return view
}
override var description: String {
return "\(Self.self)"
}
}
// ************************
// * PaymentMethod *
// ************************
protocol PaymentMethod: Order {
func selectPaymentMethod(type: String)
func inputPaymentInfo(info: String)
func setInstallmentMonth(mm: String)
var contentView: PaymentMethodView { get }
}
// 공통 PaymentMethodView
class PaymentMethodView: UIView {
public let cardNumberField = UITextField()
public let installmentMonthField = UITextField()
public let checkVaildButton = UIButton()
func fetchData(memberInfo: Member) {
cardNumberField.text = memberInfo.paymentInfo
print("\(#function) in \(Self.self)")
}
override init(frame: CGRect = .zero) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 회원 PaymentMethodView
class MemberPaymentMethodView: UIView, PaymentMethod {
func selectPaymentMethod(type: String) { }
func inputPaymentInfo(info: String) { }
func setInstallmentMonth(mm: String) { }
var contentView: PaymentMethodView {
PaymentMethodView()
}
override var description: String {
return "\(Self.self)"
}
init(frame: CGRect = .zero, memberInfo: Member) {
super.init(frame: frame)
let view = contentView
view.fetchData(memberInfo: memberInfo)
self.addSubview(view)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// 비회원 PaymentMethodView
class NonMemberPaymentMethodView: UIView, PaymentMethod {
func selectPaymentMethod(type: String) { }
func inputPaymentInfo(info: String) { }
func setInstallmentMonth(mm: String) { }
var contentView: PaymentMethodView {
PaymentMethodView()
}
override var description: String {
return "\(Self.self)"
}
}
// **************************
// * OrderViewFactory *
// **************************
protocol OrderViewFactory {
func orderView(step: OrderStep) -> Order
func orderViewController(step: OrderStep) -> OrderViewController
}
class OrderViewController: UIViewController {
fileprivate var contentView: Order
init(contentView: Order) {
self.contentView = contentView
print(self.contentView.description)
super.init(nibName: nil, bundle: nil)
}
required convenience init?(coder aDecoder: NSCoder) {
return nil
}
}
class MemberOrderViewController: OrderViewController {
}
class NonMemberOrderViewController: OrderViewController {
}
class MemberOrderViewFactory: OrderViewFactory {
var member: Member
func orderView(step: OrderStep) -> Order {
switch step {
case .address: return MemberAddressView(memberInfo: member)
case .paymentMethod: return MemberPaymentMethodView(memberInfo: member)
}
}
func orderViewController(step: OrderStep) -> OrderViewController {
var contentView = self.orderView(step: step)
return MemberOrderViewController(contentView: contentView)
}
init(member: Member) {
self.member = member
}
}
class NonMemberOrderViewFactory: OrderViewFactory {
func orderView(step: OrderStep) -> Order {
switch step {
case .address: return NonMemberAddressView()
case .paymentMethod: return NonMemberPaymentMethodView()
}
}
func orderViewController(step: OrderStep) -> OrderViewController {
let contentView = self.orderView(step: step)
return NonMemberOrderViewController(contentView: contentView)
}
}
var frontViewController: UINavigationController = .init()
// ****************
// * Client *
// ****************
enum MemberType {
case member(Member)
case non_member
}
class Client {
private let memberType: MemberType
private var factory: OrderViewFactory {
switch memberType {
case .member(let member): return MemberOrderViewFactory(member: member)
case .non_member: return NonMemberOrderViewFactory()
}
}
init(memberType: MemberType) {
self.memberType = memberType
}
/// MARK: - Presentation
func presentOrderView(type orderStep: OrderStep) {
let controller = factory.orderViewController(step: orderStep)
frontViewController.pushViewController(controller, animated: true)
}
}
let member: Member = .init()
let memberClient = Client(memberType: .member(member))
memberClient.presentOrderView(type: .address)
let nonMemberClient = Client(memberType: .non_member)
nonMemberClient.presentOrderView(type: .paymentMethod)
Conclusion
Abstract Factory 패턴을 iOS 설계에 적용해보았습니다.
긴 글 읽어주셔서 감사합니다!
'Design Pattern > GoF - Creational Patterns' 카테고리의 다른 글
[GoF Design Patterns] Singleton (0) | 2024.10.18 |
---|---|
[GoF Design Patterns] Prototype (0) | 2024.10.01 |
[GoF Design Patterns] Builder (0) | 2024.09.28 |
[GoF Design Patterns] Abstract Factory (0) | 2024.09.26 |
[GoF Design Patterns] Factory Method (0) | 2024.09.24 |