[Swift] Facade Pattern
Facade Pattern
✅ Facade Pattern
아래의 문서를 구입하여 영어 문서를 번역하고 이해한 것을 바탕으로 글을 작성하고 있습니다.
https://www.raywenderlich.com/books/design-patterns-by-tutorials/v3.0/chapters/17-facade-pattern
facade pattern은 시스템에 간단한 인터페이스를 제공하는 패턴입니다. 여기 패턴에는 두가지 유형이 포함됩니다.
1. Facade는 시스템과 상호작용하는 간단한 방법을 제공합니다. 이를 통해 소비자는 시스템의 여러 클래스에 대해 알고 상호작용 하는 대신에 Facade를 이용할 수 있습니다.
2. Dependency는 Facade가 소유한 객체입니다. 각 Dependenct는 복잡한 작업의 작은 부분을 수행합니다.
When should you use it?
이 패턴은 여러 구성 요소로 구성된 시스템이 있고, 사용자가 복잡한 작업을 수행할 수 있는 간단한 방법을 제공하려는 여우에 이 패턴을 사용하십시오.
예를 들어서, 제품 주문 시스템에는 고객 및 제품, 재고, 배송 주문 등 여러 구성 요소가 포함됩니다.
소비자가 이러한 각 구성 요소와 구성 요소가 상호작용 하는 방식을 이해하도록 요구하는 대신에, 새 주문 배치 및 이행과 같은 일반적인 작업을 노출하는 facade를 제공할 수 있습니다.
Playground example
위에서 언급한 시스템의 일부를 구현해 봅시다.
import Foundation
// MARK: - Dependencies
public struct Customer {
public let identifier: String
public var address: String
public var name: String
}
extension Customer: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
public static func ==(lhs: Customer,
rhs: Customer) -> Bool {
return lhs.identifier == rhs.identifier
}
}
public struct Product {
public let identifier: String
public var name: String
public var cost: Double
}
extension Product: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
public static func ==(lhs: Product,
rhs: Product) -> Bool {
return lhs.identifier == rhs.identifier
}
}
여기에서는 두 개의 모델을 정의합니다. Hashable을 상속받음으로써 딕셔너리 안에서 keys로 접근 가능하게끔 만듭니다.
Note: Dictionary의 키로 사용하기 위해서는 Hashable을 준수해야 합니다. 아래에서 보면 Product와 Consumer를 Key로 사용하고 있습니다.
public class InventoryDatabase {
public var inventory: [Product: Int] = [:]
public init(inventory: [Product: Int]) {
self.inventory = inventory
}
}
public class ShippingDatabase {
public var pendingShipments: [Customer: [Product]] = [:]
}
먼저 InventoryDatabase를 선언합니다. 사용 가능한 인벤토리를 저장하는 데이터베이스의 단순화된 버전으로, 특정 제품에 사용할 수 있는 항목의 수를 나타냅니다.
또한 ShippingDatabase을 선언합니다. 특정 고객을 위해 주문되었지만, 아직 보류중인 제품을 나타냅니다.
실제 서비스에서는 더 복잡하지만, 예제를 위해 간소화 했습니다.
// MARK: - Facade
public class OrderFacade {
public let inventoryDatabase: InventoryDatabase
public let shippingDatabase: ShippingDatabase
public init(inventoryDatabase: InventoryDatabase,
shippingDatabase: ShippingDatabase) {
self.inventoryDatabase = inventoryDatabase
self.shippingDatabase = shippingDatabase
}
}
여기서 OrderFace를 선언하고 inventoryDatabase 및 shippingDatabase라는 두 가지 속성을 추가합니다. 이 속성은 init(inventoryDatabase:shippingDatabase: 초기화자)를 통해 전달됩니다.
public func placeOrder(for product: Product,
by customer: Customer) {
// 1
print("Place order for '\(product.name)' by '\(customer.name)'")
// 2
let count = inventoryDatabase.inventory[product, default: 0]
guard count > 0 else {
print("'\(product.name)' is out of stock!")
return
}
// 3
inventoryDatabase.inventory[product] = count - 1
// 4
var shipments =
shippingDatabase.pendingShipments[customer, default: []]
shipments.append(product)
shippingDatabase.pendingShipments[customer] = shipments
// 5
print("Order placed for '\(product.name)' " +
"by '\(customer.name)'")
}
이것은 facade의 소비자가 주어진 product 및 customer에 대한 주문을 하는 간단한 방법입니다.
1. product.name과 customer.name를 콘솔에 나타내어 봅니다.
2. 주문을 수행하기 전에 Inventory Database에 지정된 제품이 하나 이상 있는지 확인하고, 만약 없다면 품절을 출력합니다.
3. 최소한 하나 이상 있다면, 주문을 이행할 수 있습니다. 그리고 Inventory Database에서 제품의 수를 줄일 수 있습니다.
4. 그런 다음에 제품을 shippingDatabase에 추가합니다. (지정된 고객에 대해 발송 보류상태!)
5. 마지막으로 주문이 성공적으로 이루어졌다고 프린트합니다.
다음으로는 facade를 사용해 볼 준비가 되었습니다. 다음 코드를 추가합니다.
// MARK: - Example
// 1
let rayDoodle = Product(
identifier: "product-001",
name: "Ray's doodle",
cost: 0.25)
let vickiPoodle = Product(
identifier: "product-002",
name: "Vicki's prized poodle",
cost: 1000)
// 2
let inventoryDatabase = InventoryDatabase(
inventory: [rayDoodle: 50, vickiPoodle : 1]
)
// 3
let orderFacade = OrderFacade(
inventoryDatabase: inventoryDatabase,
shippingDatabase: ShippingDatabase())
// 4
let customer = Customer(
identifier: "customer-001",
address: "1600 Pennsylvania Ave, Washington, DC 20006",
name: "Johnny Appleseed")
orderFacade.placeOrder(for: vickiPoodle, by: customer)
1. 먼저 두 가지 제품을 설정합니다. rayDoodle은 레이의 그림이고, vickiPoodle은 비키의 애완동물입니다.
2. 그런 다음 제품을 사용하여 inventoryDatabase을 생성합니다. rayDoodle은 50개가 있고, vickiPoodle은 하나 뿐이다!
3. 그런 다음에 inventoryDatabase을 사용하여, orderFacade와 새로운 ShppingDatabase를 생성합니다.
4. 마지막으로 고객을 생성하고, orderFacde.placOrder(for: by: )를 호출합니다.
콘솔에 아래와 같이 출력됩니다!
Place order for 'Vicki's prized poodle' by 'Johnny Appleseed'
Order placed for 'Vicki's prized poodle' by 'Johnny Appleseed'
What should you be careful about?
모든 앱에 "god" facade를 만드는 것에 주의하세요!
다양한 사용 사례에 대해 두 개 이상의 facade를 생성해도 좋습니다. 예를 들어 일부 클래스에서 사용하는 기능 및 다른 클래스에서 사용하는 다른 기능이 있는 두개 이상의 facade로 분할하는 것이 좋습니다.
Tutorial project
https://github.com/lgvv/DesignPattern/tree/main/facade-pattern
Key points
이번에 facade에 대해서 공부했습니다. 주요 내용은 다음과 같습니다.
- facade는 복잡한 시스템에 대한 간단한 인터페이스를 제공합니다. 두가지 타입을 제공하는데 facade와 dependency가 있습니다.
- facade는 시스템과 상호 작용할 수 있는 간단한 방법을 제공합니다. Scene 뒤에서 각각 복잡한 작업의 작은 부분을 수행하는 종속성을 소유하고 상호 작용합니다.