우리는 함수의 매개변수에 기본값이라는 것을 사용한다. 왜냐하면 어쩌다 한번 바뀌는 값을 위해 항상 매개변수로 인자 값을 전달하기 번거롭기 때문이다. protocol
을 사용하면서 protocol
함수 선언에 매개변수 기본값을 사용하는 경우 아래와 같은 에러를 마주하게 된다.
protocol SomeProtocol {
func doSomething(_ message: String = "Hellow World!") // ⛔️ Default argument not permitted in a protocol method
}
결론부터 말하면 protocol 함수 선언부에 매개변수 기본값을 사용할 수 없다.
그럼 매개변수 기본값 어떻게 사용해야 할까?
당연하게도 protocol
을 사용하지 않고 구체적인 타입만을 사용하면 매개변수 기본값을 사용할 수 있다.
struct SomeItem {
func doSomething(_ message: String = "Hello World!") {
print(message)
}
}
let s = SomeItem()
s.doSomething() // "Hello World!"
하지만 이 방법도 썩 내키지는 않는다. 왜냐하면 누구나 마음속 한쪽에 자리잡고 있는 밥 아저씨의 DIP 원칙에 따라 구체적인 타입보단 추상적인 타입에 의존해야 한다는 원칙을 지키고자 하기 때문이다.
정말 방법이 없는 것일까?
위에서 말했듯이 Swift에서 protocol
선언부에 매개변수 기본값은 사용할 수 없다. 하지만 protocol
과 extension
을 결합한다면, protocol
에서 기본값을 사용한 것과 같은 효과를 구현할 수 있다.
protocol SomeProtocol {
func doSomething(_ message: String)
}
extension SomeProtocol {
func doSomething(_ message: String = "Hello") {
print(message)
}
}
struct SomeItem: SomeProtocol {}
let s: SomeProtocol = SomeItem()
s.doSomething() // "Hello"
기존 protocol
선언부는 그대로 놔두고, extension
을 통해 기본 메서드를 구현하면 된다. 이제 우리는 DIP 원칙도 만족하면서 protocol
에서 매개변수 기본값을 가진 메서드처럼 사용할 수 있게 되었다.
만족. 😀
이번에는 protocol
을 채택하는 타입에 따라 서로 다른 메서드의 구현부가 필요한 상황을 가정하고 아래의 코드를 살펴보자.
protocol SomeProtocol {
func doSomething(_ message: String)
}
extension SomeProtocol {
func doSomething(_ message: String = "Hello") {
print(message)
}
}
struct SomeItem: SomeProtocol {
func doSomething(_ message: String) {
print("\(message) Swift!")
}
}
let s: SomeProtocol = SomeItem()
s.doSomething() // "Hello"
기본적으로 Swift에서 protocol
과 extension
을 결합하여 메서드를 기본 구현하고, protocol
를 채택한 구체 타입에서도 동일한 메서드를 직접 구현했다면, 구체화된 타입에 직접 구현된 메서드가 호출하게 된다. 하지만 s
의 타입이 protocol
타입으로 선언 되어 있고, 매개변수 기본값을 사용하는 경우 protocol
의 extension
에서 구현된 코드가 실행되는 것을 확인할 수 있었다.
이것은 아래와 같이 변수 s
가 구체적인 타입으로 선언되어 있어도 동일한 결과를 가져왔다.
let s: SomeItem = SomeItem()
s.doSomething() // "Hello"
그렇다면 매개변수 기본값을 사용하면서, 구체적인 타입에서 직접 구현한 코드를 실행하려면 어떻게 해야할까?
아래의 코드와 같이 extension
메서드 구현 내부에서 doSomething(_:)
을 다시 호출하면 protocol
을 채택한 구체 타입의 구현부가 호출된다.
protocol SomeProtocol {
func doSomething(_ message: String)
}
extension SomeProtocol {
func doSomething(_ message: String = "Hello") {
doSomething(message)
}
}
struct SomeItem: SomeProtocol {
func doSomething(_ message: String) {
print("\(message) Swift!")
}
}
let s: SomeProtocol = SomeItem()
s.doSomething() // "Hello Swift!"