TIL 0110 - 프로토콜

ian·2022년 1월 10일
0

TIL

목록 보기
7/11
post-thumbnail

음... 프로토콜을 제대로 공부하기 전에는.. 진짜 어려웠다.. 프로토콜을 왜 하는지ㅠㅠ 왜 따로 파일을 만들며 관리를 해줘야하는지..

그런데 이젠 어느정도 감이 잡혀서 기쁘다!

왜 프로토콜을 사용하는지? 에 대한 예시를 한 번 봐보자고~


이 예시는 유데미 - 안젤라 강의에서 가져왔다!


class Bird {
    var isFemale = true
    
    func layEgg() {
        if isFemale {
            print("새가 알을 낳는다.")
        }
    }
    
    func fly() {
        print("새가 하늘로 날아간다.")
    }
}


class Eagle: Bird {
    // isFamale
    // layEgg()
    // fly()
    func soar() {
        print("공중으로 치솟아 난다.")
    }
}

class Penguin: Bird {
    // isFamale
    // layEgg()
    // fly()       // 상속 구조에서는 펭귄이 어쩔 수 없이 날개됨 ⭐️ 
    func swim() {
        print("헤엄친다.")
    }
}

// struct가 될 수도 없고(클래스로만 구현가능), 무조건 Bird를 상속해야만 함
class Airplane: Bird {
    // isFamale
    // layEgg()         // 상속 구조에서는 비행기가 알을 낳게됨 ⭐️
    override func fly() {
        print("비행기가 엔진을 사용해서 날아간다")
    }
}

// 플라잉 박물관을 만듦
struct FlyingMuseum {
    func flyingDemo(flyingObject: Bird) {
        flyingObject.fly()
    }
}

let myEagle = Eagle()
myEagle.fly()
myEagle.layEgg()
myEagle.soar()

let myPenguin = Penguin()
myPenguin.layEgg()
myPenguin.swim()
myPenguin.fly()     // 문제 ===> 펭귄이 날개 됨(무조건적인 멤버 상속의 단점)

let myPlane = Airplane()
myPlane.fly()
myPlane.layEgg()         // 문제 ===> 비행기가 알을 낳음

let museum = FlyingMuseum()     // 타입 정의 ===> 오직 Bird 클래스 밖에 안됨
museum.flyingDemo(flyingObject: myEagle)
museum.flyingDemo(flyingObject: myPenguin)
museum.flyingDemo(flyingObject: myPlane)    // Bird를 상속해야만 사용 가능

상속 구조 때문에.. 팽귄이 하늘을 날고.. 비행기가 알을 낳고.. 말도 안되는 상황이 벌어진다 ㄷ ㄷ

  • "fly()"라는 동작을 따로 분리해내어서, 굳이 상속을 하지 않고도 사용가능하게 만들려면?
  • 꼭 클래스가 아닌, 구조체에서도 "fly()"기능을 동작하게 하려면?

그래서 이 걸 해결하기 위해!
우리는 프로토콜이란 걸 사용하게 된다!

// "fly()"라는 기능을 따로 분리해 내기
protocol CanFly {
    func fly()      // 구체적인 구현은 하지 않음 ===> 구체적인 구현은 자격증을 채택한 곳에서
}

class Bird1 {
    var isFemale = true
    
    func layEgg() {
        if isFemale {
            print("새가 알을 낳는다.")
        }
    }
}

class Eagle1: Bird1, CanFly {    // "CanFly" 자격증을 채택, 또! 클래스와 프로토콜을 동시에 채택하면 클래스부터 씀!!
    // isFemale
    // layEgg()
    
    func fly() {
        print("독수리가 하늘로 날라올라 간다.")
    }
   
    func soar() {
        print("공중으로 활공한다.")
    }
}

class Penguin1: Bird1 {
    // isFemale
    // layEgg()
    
    func swim() {
        print("물 속을 헤엄칠 수 있다.")
    }
}

// 구조체에서 채택도 가능
struct Airplane1: CanFly {
    func fly() {
        print("비행기가 날아간다")
    }
}

// 박물관을 만듦
struct FlyingMuseum1 {
    func flyingDemo(flyingObject: CanFly) {     // 중요한 기능 ===> 프로토콜을 타입으로 인식
        flyingObject.fly()
    }
}

let myEagle1 = Eagle1()
myEagle1.fly()
myEagle1.layEgg()
myEagle1.soar()

let myPenguin1 = Penguin1()
myPenguin1.layEgg()
myPenguin1.swim()
//myPenguin1.fly()     // 더이상 펭귄이 날지 않음

let myPlane1 = Airplane1()
//myPlane1.layEgg()         // 더이상 비행기가 알을 낳지 않음
myPlane1.fly()

let museum1 = FlyingMuseum1()
museum1.flyingDemo(flyingObject: myEagle1)
//museum1.flyingDemo(flyingObject: myPenguin1)   // 더이상 "CanFly"자격증이 없는 펭귄은 날지 못함
museum1.flyingDemo(flyingObject: myPlane1)

이렇게 효율적으로 사용할 수 있겠죠잉?


그런데, 프로토콜도 문법이 있다는 사실!
사실 프로토콜이 이해가 잘 안됐을 땐 자격증 이라고 생각하며 공부했어요.

프로토콜의 문법은

  • 정의하기
  • 채택하기
  • 구현하기

순으로 이루어 져있습니다!

정의하기 부터 확인하자면

protocol MyProtocol {   // 최소한의 요구사항 나열
    func doSomething() -> Int
}

딱! 이정도만 구현하면 돼요 더는 노노.

채택하기

class MyClass: FamilyClass, MyProtocol {    // 2) 상위클래스인 FamilyClass를 먼저 선언
    
}

이렇게 구현하고,

구현하기

class MyClass: FamilyClass, MyProtocol { 
    // 3) (속성/메서드) 구체적인 구현
    func doSomething() -> Int {
        return 2
    }
}

class, struct, enum 내부에 자세히 구현할 것을 구현하면 됩니다!


그리고 엄청 재미있는 예시를 봤음..
바로 스위치? 같은 예시인데 맛만 보자고!

protocol Togglable {
    mutating func toggle()   // mutating의 키워드는 메서드 내에서 속성 변경의 의미일뿐(클래스에서 사용 가능)
}

위 같은 프로토콜을 만든 후,

enum OnOffSwitch: Togglable {
    case on
    case off
    
    mutating func toggle() {
        switch self {   // .on   .off
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}

var s = OnOffSwitch.off
s.toggle()
s.toggle()

class BigSwitch: Togglable {
    var isOn = false
    
    func toggle() {      // mutating 키워드 필요없음 (클래스 이기 때문)
        isOn = isOn ? false : true
    }
}

var big = BigSwitch()
print(big.isOn)
big.toggle()
print(big.isOn)

과 같이 구현할 수 있다! 약간.. 다크모드, 라이트모드을 구현할 때 이 방법이 잘 어울리는 것 같다.


학습 키워드

  • 프로토콜

문제점 / 고민한 점

  • extension 같은 경우에는.. 본체에다 하는게 나을까? 아님 확장에다 하는게 나을까? -> 확장에 하는게 더 낫다고 합니다요 ^^..

TIL 시리즈는 막연히 제가 배운 걸 기록하는 공간입니다.


출처:
앨런 swift 문법 마스터 스쿨

profile
디자인씽킹을 하며 iOS 를 공부합니다

0개의 댓글