4T2F / ThinkBig

🌟씽크빅 스터디🌟
5 stars 1 forks source link

프로토콜 지향 프로그래밍(POP)이란 무엇이며, 어떤 장점이 있나요? #60

Open withseon opened 5 months ago

withseon commented 5 months ago

프로토콜 지향 프로그래밍(POP)이란 무엇이며, 어떤 장점이 있나요?

Swift에는 3가지 특징이 있다.

객체 지향 프로그래밍, 프로토콜 지향 프로그래밍, 함수형 프로그래밍

오늘은 그 중 프로토콜 지향 프로그래밍에 대해 알아보도록 하자.



프로토콜

Protocol은 규약, 협약을 뜻한다. 특정한 요구 사항(속성, 메서드)를 가진 프로토콜을 정의하고 클래스, 구조체, 열거형을 선언할 때 이를 채택함으로써, 해당 요구 사항(속성, 메서드)를 충족시키도록 한다.

다음은 간단한 프로토콜 사용의 예시이다.

  1. 프로토콜 정의
protocol CanFly {
    func fly() // 요구사항
}
  1. 프로토콜 채택 및 요구 사항 구체적 구현
class Bird {
    var isFemale = true
    func layEgg() {
        print("새가 알을 낳는다.")
    }
}

class Egle: Bird, CanFly { // Bird를 상속하고 CanFly를 채택
    func fly() { // CanFly 프로토콜 메서드 구현
        print("독수리가 하늘로 날아올라 간다.")
    }
    func soar() { // 상속 받아서 메서드 추가
        print("공중으로 활공한다.")
    }
}

class Penguin: Bird { // Bird 상속
    func swim() { // 상속 받아서 메서드 추가
        print("물 속을 헤엄칠 수 있다.")
    }
}

struct Airplane: CanFly { // CanFly 채택
    func fly() { // CanFly 프로토콜 메서드 구현
        print("")
    }
}

struct FlyingMuseum {
    func flyingDemo(flyingObject: CanFly) { // 프로토콜을 타입으로 사용
        flyingObect.fly()
    }
}



그렇다면, 프로토콜을 지향하면 무엇이 좋을까? 프로토콜 지향 프로그래밍이 나타난 배경에 대해 알아보자.



프로토콜 지향 프로그래밍이 탄생하게 된 배경에는 객체 지향 프로그래밍이 있다. (객체 지향 프로그래밍이 메인이 아니니 간단히 살펴보자.)

객체 지향 프로그래밍 프로그램을 객체 단위로 분할하여 설계하고 구현하는 방법으로 상속, 캡슐화, 다형성, 추상화의 특징을 가지고 있다.



객체 지향 프로그래밍의 단점이 있는데,

  1. 클래스의 상속은 단일 상속만 가능하다.
  2. 상속을 하게 되면 원치 않는 속성이나 메서드를 포함해야 할 가능성이 있다.
  3. 클래스 타입만 상속이 가능하다.

라는 것이다.

프로토콜 지향 프로그래밍은 이러한 단점을 보완할 수 있다.



프로토콜 지향 프로그래밍의 장점

  1. 여러 개의 프로토콜을 채택할 수 있다. (다중 상속과 유사)
protocol MyProtocol {
    func doSomething() -> Int
}

class FamilyClass { }

// 클래스
class MyClass: FamilyClass, MyProtocol {
    func doSomething() -> Int {
    }
}



protocol Remote {
    func turnOn()
    func turnOff()
}

protocol AirConRemote {
    func Up()
    func Down()
}

protocol SuperRemoteProtocol: Remote, AirConRemote {
    func doSomething()
}

// 채택
class HomePot: SuperRemoteProtocol {
    // 위의 다섯가지 요구사항에 대해 구현해야 함
}



  1. 메모리 구조에 대한 특정 요구 사항이 없다. 선택적 요구 사항을 정의할 수 있다.
@objc protocol Remote {
    @objct optional var isOn: Bool { get s et} // 선택적 요구 사항
    func turnOn() // 필수 요구 사항
}



  1. 확장을 통해 메서드의 기본 구현을 제공한다.
protocol Remote {
    func turnOn()
    func turnOff()
}

class Aircon: Remote {
    func turnOn() { print("전원을 켰습니다.") }
    func turnOff() { print("전원을 껐습니다.") }
    func changeAct() { print("냉난방을 전환했습니다.") }
}

class TV: Remote {
    func turnOn() { print("전원을 켰습니다.") }
    func turnOff() { print("전원을 껐습니다.") }
    func changeChannel() { print("채널을 바꿨습니다.") }
}
protocol Remote {
    func turnOn()
    func turnOff()
}

extension Remote {
    func turnOn() { print("전원을 켰습니다.") }
    func turnOff() { print("전원을 껐습니다.") }
}

class Aircon: Remote {
    // turnOn, turnOff 기본 구현 되어있음
    func changeAct() { print("냉난방을 전환했습니다.") }
}

class TV: Remote {
    // turnOn, turnOff 기본 구현 되어있음
    func turnOn() { print("TV 전원을 켰습니다.") }
    func changeChannel() { print("채널을 바꿨습니다.") }
}

let tv = TV()
tv.turnOn() // TV 전원을 켰습니다.
tv.turnOff() // 전원을 껐습니다.



  1. 프로토콜은 일급 객체로, 활용성이 높다.
protocol Remote {
    func turnOn()
    func turnOff()
}

struct SetTopBox: Remote {
    func turnOn() {
        // 구현
    }
    func turnOff() {
        // 구현
    }
    func doNetflix() { }
}

// 프로토콜 타입으로 변수 선언 가능
let sbox: Remote = SetTopBox()
sbox.turnOn()
sbox.turnOff()
// sbox.doNetflix() // ERROR. 접근 불가
(sbox as! SetTopBox).doNetflix() // 다운캐스팅으로 접근


let some: AProtocol & BProtocol = 두 프로토콜을 모두 채택한 타입의 인스턴스



프로토콜이 지향 프로그래밍이 탄생한 배경과 함께 프로토콜의 특징과 장점에 대해 살펴봤다. 정리해보면 다음과 같다.

프로토콜을 다양한 타입에서 재사용할 수 있다. 필요에 따라 확장을 통해 기능을 추가할 수 있기 때문에, 유연성과 확장성이 높다. 프로토콜 확장을 사용함을로써 코드의 중복을 줄일 수 있으며 유지보수성이 높아진다. 프로토콜 사용으로 구현을 추상화하여 필요한 부분만 테스트할 수 있다. 따라서, 테스트 용이성이 좋다.



ha-nabi commented 5 months ago

객체 지향 프로그래밍의 단점이 있는데,

클래스의 상속은 단일 상속만 가능하다. 상속을 하게 되면 원치 않는 속성이나 메서드를 포함해야 할 가능성이 있다. 클래스 타입만 상속이 가능하다.

객체 지향 프로그래밍의 단점 중에서 클래스의 상속은 단일 상속만 가능하다. 라고 말하셨는데 구조체는 불가능한 이유를 알 수 있을까요?