오늘은 Composite 패턴에 대해 알아보겠습니다.
Composite 패턴은 전체 계층 구조에서 부분적인 객체를 동일하게 취급할 수 있도록 하는 패턴입니다.
해당 객체를 사용하는 Client 입장에서는 '전체'나 '부분'이나 동일한 인터페이스로 인식할 수 있는 계층 구조를 만드는 것입니다.
패턴 적용 전
class Item {
private var name: String
private var price: Int
init(name: String, price: Int) {
self.name = name
self.price = price
}
}
class ShoppingCart {
private var items = [Item]()
func addItem(item: Item) {
items.append(item)
}
func getItems() -> [Item] {
return self.items
}
}
Item과 ShoppingCart 두 개의 타입이 있습니다.
Client 쪽에서 해당 타입을 생성해 가격을 확인하는 코드를 작성해 보겠습니다.
class Customer {
let myCart = ShoppingCart()
func buyItem(item: Item) {
myCart.addItem(item: item)
}
func calculateItemPrice(item: Item) -> Int {
return item.getPrice()
}
func calculateAllItemPrice() -> Int {
let priceArray = myCart.getItems().map { Item in
Item.getPrice()
}
return priceArray.reduce(0) { $0 + $1 }
}
}
let customer = Customer()
let item = Item(name: "라면", price: 1000)
let cookie = Item(name: "과자", price: 1500)
let juice = Item(name: "음류수", price: 800)
customer.buyItem(item: item)
customer.buyItem(item: cookie)
customer.buyItem(item: juice)
customer.calculateItemPrice(item: item)
customer.calculateAllItemPrice()
위 코드에서 보다 싶이 ShoppingCart에 담긴 물건에 가격을 얻거나 물건 하나에 대한 가격을 알고 싶다면 따로 메서드를 생성해 주어야 합니다.
또 한 Customer는 ShoppingCart에 대한 정보만 알고 있고ShoppingCart에 정확히 어떤 Item들이 담겨있는지 알 수 없습니다. 즉, Item 들에 대한 정보를 알고 있는 ShoppingCart에서 전체 가격을 리턴하는 메서드를 가지고 있어야 할 것 같습니다.
(이전에 책에서 본 내용 중에 책임 할당의 기본 원칙을 여기서 지켜야 하는 게 아닐까라는 생각을 합니다.)
책임 할당의 기본 원칙 : 책임을 수행하는 데 필요한 정보를 가진 객체에서 그 책임을 할당하는 것이다.
패턴 적용
protocol Component {
func getPrice() -> Int
}
Component에는 Client에서 사용할 공통된 기능을 정의합니다.
class Item: Component {
private var name: String
private var price: Int
init(name: String, price: Int) {
self.name = name
self.price = price
}
func getPrice() -> Int {
return self.price
}
}
class ShoppingCart: Component {
private var items = [Item]()
func addItem(item: Item) {
items.append(item)
}
func getItems() -> [Item] {
return self.items
}
func getPrice() -> Int {
let priceArray = items.map { Item in
Item.getPrice()
}
return priceArray.reduce(0) { $0 + $1 }
}
}
Leaf는 이제 부분에 해당하고 Composite는 Leaf를 가지고 있는 전체에 해당합니다.
class Customer {
let myCart: ShoppingCart
init(myCart: ShoppingCart) {
self.myCart = myCart
}
func buyItem(item: Item) {
myCart.addItem(item: item)
}
func calculatePrice(item: Component) -> Int {
return item.getPrice()
}
}
Client 코드에서는 이제 Item에 대한 가격이나 ShoppingCart에 저장된 Item 가격의 총합을 하나의 메서드를 통해 알 수 있습니다.
해당 글은 인프런의 코딩으로 학습하는 GoF 디자인 패턴 강의를 참고해 작성했습니다.
⭐️ 부족하거나 잘못된 부분이 있다면 댓글은 언제나 환영입니다!! ⭐️