Swift 프로토콜과 extension

  1. 프로토콜이란? 오직 인터페이스만 있는 상태로 구현하는 부분없이 프로퍼티나 메소드를 선언한 형태로 정의된 API를 의미한다.
    1. 프로토콜 정의와 채택(사용)
      1. 프로토콜 정의
        프로토콜 정의
        protocol PROTOCOL_NAME {
        // 프로퍼티 선언
        // 메소드 선언
        }
      2. 프로토콜 채택 : 클래스나 구조체에서 정의된 프로토콜을 이용하는 것을 프로토콜을 채택했다고 한다. 프로토콜 채택 시에는 프로토콜에 정의된 프로퍼티, 메소드는 반드시 클래스나 구조체에서 구현이 되어야 한다.
        프로토콜 채택
        // 프로토콜 정의
        protocol PROTOCOL_NAME {
        // 프로퍼티 선언
        // 메소드 선언
        }

        // 클래스에서 프로토콜 채택
        class CLASS_NAME : PROTOCOL_NAME {
        }
        // 구조체에서 프로토콜 채택
        struct STRUCT_NAME : PROTOCOL_NAME {
        }
      3. 클래스의 상속과 프로토콜 : 클래스에서는 부모 클래스를 상속하고, 프로토콜 채택을 모두 할 수 있으며 아래와 같이 정의한다.
        클래스 상속과 프로토콜
        // 프로토콜 정의
        protocol PROTOCOL_NAME {
        // 프로퍼티 선언
        // 메소드 선언
        }
        // 부모 클래스
        class PARENT_CLASS {
        }

        // 자식 클래스에서 부모 클래스 상속과 프로토콜 채택
        class CHILD_CLASS : PARENT_CLASS, PROTOCOL_NAME {
        }
        /* 부모 클래스를 먼저 쓰고, 그 다음에 콤마 다음에 프로토콜을 쓴다.*/
      4. 프로토콜 상속 : 프로토콜은 다중 상속이 가능하여 여러 프로토콜을 상속할 수 있다.
        프로토콜 정의
        protocol Singing {
           func sing() {}
        }

        protocol Dancing {
           func dance() {}
        }
        // Singing, Dancing에 정의된 메소드를 모두 구현해야한다.
        protocol Entertaining : Singing, Dancing {
        }

        // Entertaining 프로토콜 채택 시 상속된 모든 메소드를 구현
        class Human : Entertaining {
            func sing() {print("라라라")}
            func dance() {print("둠칫둠칫")}
        }
      5. 프로토콜 내 프로퍼티 선언 : 프로퍼티의 타입 선언 가능하고, 구현 시에 저장 프로퍼티로 사용할지 계산 프로퍼티로 사용할지 프로토콜 채택 후에 정한다.
        프로퍼티 선언
        protocol Singing {
            // 프로퍼티 선언
            var duration : Int {get set}
        }

        // 저장 프로퍼티로 구현
        struct MyStruct : Singing {
           var duration : Int
        }

        // 계산 프로퍼티로 구현
        class MyClass : Singing {
           var duration : Int {
              get { return 0 }
              set { }
           }
        }
      6. 프로토콜 내 Initializer 선언 : 프로토콜 내에 Initializer 선언이 가능하고, 클래스에서 구현 시에는 required를 추가한다.
        프로토콜 내 Initializer 구현
        protocol Singing {
            var sing : String
            // Initializer
            init(sing : String)
        }

        // 클래스가 프로토콜을 채택 시 Initializer 구현
        class Monster : Singing {
            required init(sing : String) {
                self.sing = sing
            }
        }

        // 구조체가 프로토콜을 채택 시 Initializer 구현
        struct Boss : Singing {
            init(sing : String) {}
        }
      7. 프로토콜을 타입으로 사용 시 채택한 클래스나 구조체에 별도의 메소드가 구현되어 있어도, 프로토콜 내의 메소드, 프로퍼티만 사용 가능하다.
        프로토콜 내 Initializer 구현
        protocol Singing {
            // 메소드
            func sing() {}
        }

        // 클래스가 채택하여 구현
        class Human : Singing {
            func sing() {
               print("랄랄랄")
            }

            func dancing() {
               print("쿵쿵쿵")
            }
        }

        // 프로토콜을 타입으로 사용
        var singingAnimal : Singing = Human()
        singingAnimal.sing()
        singingAnimal. dancing() // 사용 못함
  2. extension(확장)
    1. 기존에 작성된 타입(클래스나 구조체, enum)을 확장해서 사용할 수 있다.
    2. extension을 할 경우 하나의 타입으로 동작한다.
      extension 예시
      class Dog{
         func eat() {
            print("뭐든 잘 먹어요.")
         }
      }

      // 확장
      extension Dog {
          func sing() {
              print("멍~멍~")
          }
      }

      var myDog = Dog()
      myDog.eat()
      myDog.sing() // 확장한 메소드 사용 가능
    3. extension이 가능한 것과 불가능한 것
      1. 가능한 것
        1. convenience initializer
        2. 계산 프로퍼티
        3. 메소드
        4. 프로토콜
        5. 서브스크립트
        6. nested type
      2. 불가능한 것
        1. designated initializer
        2. 저장 프로퍼티
  3. 프로토콜과 프로토콜 extension
    1. 기존에 정의된 프로토콜을 확장하여 사용 가능하며, 프로토콜의 다중 상속을 이용하여 코드 중복을 없애는데 사용된다.
    2. 코드 중복이 발생되는 상황
      코드 중복 발생
      class Bird{
         func move() {
             print("날아서 이동")
         }
         func sound() {
             print("소리를 낸다.")
         }
      }

      class Airplane{
         func move() {
             print("날아서 이동")
         }
         func sound() {
             print("소리를 낸다.")
         }
      }
    3. 코드 중복을 없애는데 프로토콜 확장을 사용
      프로토콜 확장으로 코드 중복 제거
      protocol Movable() { }
      extension Movable {
         func move() {print("날아서 이동")}
      }

      protocol Soundable {}
      extension Soundable {
         func sound() {print("소리를 낸다.")}
      }

      // 중복되는 메소드를 프로토콜 확장으로 제거
      class Bird : Movable, Soundable {
      }

      class Airplane : Movable, Soundable {
      }