좋은 설계란 기본적으로 시스템에 새로운 요구사항이나 변경이 있을 때 가능한 한 영향 받는 부분을 최소화한 설계이다.
//MARK: enum
/*
만약, 더블치즈 버거를 추가하고 싶다면?
*/
enum Hamburger {
case cheese
case avocado
case doubleCheese
}
class OrderHamburger {
let hamburger: Hamburger
init(hamburger: Hamburger) {
self.hamburger = hamburger
}
func order() {
switch hamburger {
case .cheese:
print("치즈버거 주세요")
case .avocado:
print("아보카도버거 주세요")
}
}
}
먼저 enum 으로 설계를 한 코드이다.
enum도 구체적인 햄버거 종류를 case 로 설정을 해줄수 있다.
하지만 만약 doubleCheese
처럼 햄버거 종류가 추가된다면 어떻게 될까?
위와 같은 오류를 만나게 된다😁
enum으로 구현한 case를 구현해줘야하기 때문이다.
추가를 하게 되면, order
메서드에 case 를 추가하면서 기존 코드를 수정하게 된다.
이러면에서 유연성이 낮은 것을 알수 있다.
추상화와 상속을 통해 해결할 수 있다.
swift 에서는 protocol
을 사용할 수 있다.
//MARK: OCP ; Protocol 활용하여 확장은 쉽게 (확장은 열려있고), 그리고 확장시 기존 코드 수정하지 않게끔 (변경은 닫혀있고)
/*
- 추상화와 상속 통해 구현 가능
- 유연함 증가
*/
protocol HamburgerProtocol {
func order()
}
struct Cheese: HamburgerProtocol {
func order() {
print("치즈버거 나왔어요")
}
}
struct Avocado: HamburgerProtocol {
func order() {
print("아보카도버거 나왔어요")
}
}
//더블치즈 추가하면?
struct DoubleCheese: HamburgerProtocol {
func order() {
print("더블치즈버거 나왔어요")
}
}
class OrderHamburger2 {
let hamburger: HamburgerProtocol
init(hamburger: HamburgerProtocol) {
self.hamburger = hamburger
}
func orderHamburger() {
hamburger.order()
}
}
OrderHamburger2(hamburger: DoubleCheese()).orderHamburger() //더블치즈버거 나왔어요
OCP를 적용하기 위해 HamburgerProtocol
를 프로토콜로 둠으로써, 햄버거 종류가 많아진다해도 생긴다고 해도 HamburgerProtocol
를 채택하기만 하면 OrderHamburger2
클래스에서는 변경해야할 코드가 없다.
즉, 결합도가 낮아져 유지보수가 쉬워졌다.