study record
[Swift] 프로토콜 지향 프로그래밍 본문
*이 글은 책 “스위프트 프로그래밍”을 읽고 작성된 글입니다.
객체지향 프로그래밍 패러다임에 기반을 둔 언어는 대부분 클래스의 상속을 사용해 타입에 공통된 기능을 구현한다. 그런데 스위프트의 표준 라이브러리에서 타입과 관련된 것은 대부분 구조체로 구현되어 있다. 상속이 되지 않는 구조체로 어떻게 다양한 공통 기능을 가질 수 있는지는 프로토콜, 익스텐션, 제네릭의 조화를 통해 이루어진다.
프로토콜 초기 구현
익스텐션은 기존 타입의 기능을 확장하고, 프로토콜은 프로토콜을 채택한 타입이 원하는 기능을 강제로 구현한다. 익스텐션과 프로토콜의 결합을 통해 중복 코드와 유지보수를 해결한다.
프로토콜을 정의할 때는 그 프로토콜을 채택한 타입에서 구현해주어야 하는 프로토콜의 요구사항을 구현할 수 없다. 단지 요구사항을 정의만 할 수 있다. 그러나 프로토콜의 익스텐션에는 프로토콜이 요구하는 기능을 실제로 구현할 수 있다. 다만 익스텐션에는 저장 프로퍼티를 구현할 수 없으므로 저장 프로퍼티는 각각의 타입에서 직접 구현해야 한다. 이렇게 프로토콜과 익스텐션을 결합하면 코드의 재사용성이 증가한다.
이처럼 프로토콜의 요구사항을 익스텐션을 통해 구현하는 것을 프로토콜 초기구현이라고 한다.
만약 프로토콜의 익스텐션에서 구현한 기능을 사용하지 않고 타입의 특성에 따라 변경하고 싶다면 재정의하면 된다.
맵, 필터, 리듀스 직접 구현해보기
맵은 컨테이너가 담고 있던 각각의 값을 매개변수를 통해 받은 함수에 적용한 후 다시 컨테이너에 포장하여 반환하는 함수이다. 필터는 컨테이너 내부의 값을 걸러서 추출해 새로운 컨테이너에 값을 담아 반환하는 함수이다. 리듀스는 컨테이너 내부의 콘텐츠를 하나로 합쳐주는 기능을 실행하는 함수이다.
//// Stack 구조체 구현부
func map<T>(transform: (Element) -> T) -> Stack<T> {
var transformedStack: Stack<T> = Stack<T>()
for item in items {
transformedStack.items.append(transform(item))
}
return transformedStack
}
//// Stack 구조체 구현부 외부
var myIntStack: Stack<Int> = Stack<Int>()
myIntStack.push(1)
var myStrStack: Stack<String> = myIntStack.map{ "\($0)" }
transform 함수는 Stack 요소의 타입인 Element의 값을 T 타입으로 변환한 후 자신이 속해 있는 타입고 ㅏ같은 컨테이너인 Stack의 모습으로 결과를 반환한다.
기본 타입 확장
프로토콜 초기구현을 통해 스위프트의 기본 타입을 확장하여 내가 원하는 기능을 공통적으로 추가해볼 수도 있다. 스위프트 표준 라이브러리에 정의되어 있는 타입은 실제 구현코드를 보고 수정할 수 없기 때문에 익스텐션, 프로토콜, 프로토콜의 초기구현을 사용해 기본 타입에 기능을 추가해 볼 수 있다.
protocol SelfPrintable {
func printSelf()
}
extension SelfPrintable {
func printSelf(){
print(self)
}
}
extension Int: SelfPrintable { }
1024.printSelf() // 1024
'Swift > 스위프트 프로그래밍' 카테고리의 다른 글
[Swift] 패턴 (0) | 2022.01.20 |
---|---|
[Swift] 타입 중첩 (0) | 2022.01.19 |
[Swift] 제네릭 (0) | 2022.01.14 |
[Swift] 익스텐션 (0) | 2022.01.12 |
[Swift] 프로토콜 (0) | 2022.01.11 |