프로토콜 지향 프로그래밍은 Swift 2.0에서 소개된 새로운 프로그래밍 패러다임이다.
프로토콜 확장과 프로토콜 상속, 그리고 프로토콜 합성이라는 새로운 개념들을 사용하여 프로토콜의 정의를 통해 시스템을 설계한다.
이러한 패러다임은 값 타입(구조체, 열거형)을 선호하는 Swift 특성에도 알맞다.
객체 지향 패러다임 기반의 이전 언어들은 대부분 클래스의 상속을 사용해 타입에 공통된 기능을 구현한다.
반면 Swift의 타입들은 대부분 상속이 불가능한 타입인 구조체로 구현되어 있다.
이들 값 타입들은 프로토콜의 채택과 확장을 통해 공통 기능을 가질 수 있게 되었다.
소프트웨어 시스템을 설계할 때, 먼저 주어진 시스템의 요구사항을 충족하는 데 필요한 요소(elements)들을 찾아내게 된다.
그 후 이러한 요소들 사이의 관계(relationship)를 모델링한다.
그 방법으로는 슈퍼클래스(superclass)를 정의한 후 상속을 통해 관계를 모델링할 수도 있고 (클래스 기반), 프로토콜을 정의한 후 프로토콜 채택을 통해 관계를 모델링할 수도 있다 (프로토콜 기반).
애플에서는 프로토콜을 사용한 모델링을 권장한다.
결론적으로 위와 같은 장점들은 프로그래밍에 있어서 주요한 과제인 복잡성(complexity)의 관리를 가능하게 한다.
그러나... Swift에서는 구조체와 열거형도 일급 객체(first class citizen)이기 떄문에 위 장점들이 사용 가능하다.
일급 객체란?
일급 객체란 아래 조건을 모두 만족하는 객체이다.
- 변수에 저장하거나 할당할 수 있다.
- 함수의 파라미터(인자)로 전달될 수 있다.
- 함수의 결과로서 반환될 수 있다.
암시적 공유 (Implicit Sharing)
위와 같은 상황에서 A는 B에 영향을 끼치지 않고 Data를 변경할 수 없다. 이를 방지하기 위해 Data의 복사본을 만들 수 있지만, 비효율적이다.
값 타입은 공유하지 않고 복사하기 때문에 위와 같은 일이 일어나지 않는다.
클래스의 상속은 깔끔하지 못함 (class inheritance is too intrusive)
클래스는 단 하나의 상위 클래스만 가진다. 따라서 monolithic 하다.
만약 여러 개의 추상화를 모델링해야 한다면? 클래스로는 불가능하다.
클래스에서는 collection과 serialized가 동시에 될 수 없다.
또한 클래스가 단일 상속만을 지원하기 때문에, 클래스는 자신과 연관이 없거나 '있을 지도 모르는' 상위 클래스의 모든 프로퍼티와 메서드를 상속해야 하고, 쓰이지 않을 저장 속성을 초기화해야 한다.
이를 해결하기 위해 Swift에서는 Delegate 패턴을 사용한다.
타입 관계성을 상실함 (lost type relationships)
클래스를 통해서는 그 자신의 타입과 다른 타입간의 중요한 관계를 표현할 수 없다.
이러한 단점들을 해결하기 위해서는 더 나은 추상화 방식이 필요하다. -> 프로토콜의 등장
프로토콜의 장점
위 장점들을 통해 프로토콜이 클래스보다 나은 추상화를 제공하기 때문에 애플에서는 클래스보다 프로토콜을 사용한 모델링을 권장한다.
Swift에서 클래스는 다중 상속을 지원하지 않기 때문에, 시스템 설계 중 다른 클래스에서의 기능이 필요한 경우가 생긴다면(상속해야 할 경우가 생긴다면) 슈퍼클래스에 관련된 기능들을 계속해서 추가하거나 슈퍼클래스를 상속하는 새로운 클래스를 만들어 관련 기능을 추가한 후, 그 클래스를 상속해야 한다.
반면 프로토콜은 상위-하위의 상속 관계보다는 설계도와 같이 작동한다. 프로토콜 모델들은 구현 타입들이 무엇을 구현해야 하는지 알려줌으로써 추상화를 이루어낸다.
앞서 말했듯이 프로토콜은 다중 상속이 가능하기 때문에, 하나의 타입으로 여러 개의 추상화를 모델링할 수 있다. 이는 상위 클래스의 기능에 의존하게 되는 클래스의 추상화보다 훨씬 나은 방법이다. 세분화된 추상화를 통해 기능의 분리와 확장, 추가가 자유로워지기 때문이다.
클래스는 수직적이고 프로토콜은 수평적이다. 즉, 서로 다른 방식으로 실제 세계를 추상화한다.
수직적 관계가 무수히 중첩되어도 코드를 관리하기 어려워지지만, 굉장히 많은 여러 개의 작은 프로토콜들의 수평적 관계도 코드를 난잡하게 만든다.
결국 프로그래머는 주어진 문제를 어떤 방식으로 해결하는 것이 더 알맞은 것인지 고민해서 해결 방법을 적용해야 한다.
그러나 어떤 기준으로 POP와 OOP를 적용하는지에 대해서는 더 많은 공부와 경험이 필요하다.
다음 글은 구조체와 클래스 둘 중 어떤 경우에 무엇을 사용하는지에 대해 알아보겠다. (관련 링크)
참고 : https://jayb-log.tistory.com/261?category=1003462, https://www.wwdcnotes.com/notes/wwdc15/408/