객체지향과 함수형 - ChoDragon9/posts GitHub Wiki
프로토콜은 Swift에서 제공하는 기능이다. TypeScript에서 interface와 유사하며, 엄격하게 비교하면 다른점이 있다. 하지만 TypeScript에서 프로토콜 지향 프로그래밍을 하기에는 interface가 적당하다고 생각한다.
순서
- 프로토콜 지향 프로그래밍
- 프로그래밍 패러다임의 지향점
프로토콜 지향 프로그래밍
Swift에서 등장한 프로그래밍 패러다임이다. 기존에 클래스로 이뤄진 문제점을 해결하기 위해 프로토콜 지향 프로그래밍으로 전환했으며, 많은 Swift의 표준 라이브러리 프로토콜로 이뤄져있다.
protocol
키워드로 필드와 메소드 스팩을 선언해서 class, struct, enum에 확장할 수 있다. Swift는 클래스로 구현된 타입보다는 구조체로 기본 타입이 구현되어 있다.
프로그래밍 패러다임의 지향점
클린 아키텍처에서는 패러다임을 이렇게 해석한다.
패러다임은 무엇을 해야 할지를 말하기보다는 무엇을 해서는 안 되는 지를 말해준다.
실무나 논의를 하다보면 무엇을 해야 할지에 집중을 하게 된다. 이번에는 각 패러다임에서 무엇을 하면 안되는 지 그리고 어떻게 해결했는 지에 대해서 작성해봤다.
객체지향
계층으로 구성된 모듈관계에서 의존성과 제어흐름은 고수준에서 저수준 방향으로 이뤄진다. 이 의미는 고수준과 저수준의 코드가 강한 의존성을 가지면 변경이 힘들다는 것을 의미한다.
객체지향에서는 인터페이스를 통해 의존성 역전 현상을 통해 해결했다. 고수준은 인터페이스를 통해 저수준을 사용하고, 저수준은 인터페이스를 의존하는 것이다. 그렇게 되면 소스 코드 의존성에 대한 절대적인 제어 권한을 획득할 수 있게 된다.
함수형
경합 조건, 교착상태 조건, 동시성 업데이트 문제가 모두 가변 변수로 인해 발생한다.
함수형에서는 이러한 문제를 해결하기 위해 가변성과 불변성을 분리한다. 가변성이 있는 영역에서는 이벤트 소싱과 같은 불변성 상태를 통해 문제를 해결한다.
프로토콜지향
클래스 상속의 한계를 해결한다. 서로 다른 클래스에서 상속받은 클래스는 동일한 기능을 구현하기 위해 중복된 코드가 발생한다. 다중 추상화 모델이 필요하면 클래스 상속으로는 해결하기 힘들다.
Swift에서는 protocol
과 extension
을 통해서 해결했다. 공통된 스팩은 protocol
에 정의하며 공통된 구현은 extension
에 정의한다. 그리고 정의된 protocol
은 클래스, 구조체, 열거형에 사용할 수 있다.
추상화
어플리케이션을 설계할 때 어떤 도구를 통해서 설계를 하는지에 대한 설명이다.
객체지향
객체를 추상화 도구로 사용한다.
함수형
함수를 추상화 도구로 사용한다.
프로토콜지향
프로토콜을 추상화 도구로 사용한다.
상태관리
객체지향
함수형
프로토콜지향
루틴
루틴은 어떤 절차를 묶어 호출 가능하도록 이름를 부여한 기능 모듈을 의미한다. 루틴은 다시 프로시저와 함수로 구분할 수 있다. 프로시저는 정해진 절차에 따라 내부의 상태를 변경하고 함수는 어떤 절차에 따라 필요한 값을 계산해서 반환한다.
객체지향
객체지향에서는 프로시저와 함수를 같이 사용한다. 하지만 두개를 정확히 분리해서 사용하는 명령-쿼리 분리 원칙 준수를 권고한다.
함수형
함수형은 함수를 사용하여 상태 변경을 최소화하는 것을 지향한다.
프로토콜지향
extension
로 함수와 프로시저를 정의한다.
중복 코드를 제거하는 방법
객체지향
상속을 통해 중복을 해결한다. 구현 상속과 인터페이스 상속이 있는 데 구현를 재사용 목적일 때는 구현 상속을 하고 인터페이스를 상속할 때는 인터페이스 상속을 한다.
하지만 상속은 캡슐화 약화와 설계가 유연하지 않다는 두가지 단점이 있다. 이런 경우는 합성이라는 개념을 사용한다.
함수형
행위에 대한 함수를 추상화한다.
프로토콜지향
protocol
로 정의한 요구사항을 extension
을 통해 구현한다.
protocol Walkable {
var isBareFoot: Bool { get set }
func walk(name: String)
}
extension Walkable {
func walk(name: String) {
if isBareFoot == true {
print("\(name)은 맨발")
} else {
print("\(name)은 신발신음")
}
}
}
struct Person: Walkable {
var isBareFoot: Bool
}
struct Animal: Walkable {
var isBareFoot: Bool
}
let peter = Person(isBareFoot: false)
peter.walk(name: "피터")
let dog = Animal(isBareFoot: true)
dog.walk(name: "몽몽이")