[Swift] 프로토콜 - 3 ( Protocol )

프로토콜 - 3 (Protocol)

확장을 통한 프로토콜 사용

기존 타입의 코드에 접근할 수 없는 경우에도 기존 타입을 확장하여 새로운 프로토콜을 채택하고 준수(구현)할 수 있다.

가능한 이유는 확장은 기존 타입에 새 연산 프로퍼티, 메서드, subscript를 추가할 수 있기 때문에

프로토콜의 요구사항을 추가하고 구현할 수 있다.

protocol Introduce {
    var infoString: String { get }
}

extension Int: Introduce {
    var infoString: String { "\(self)" }
}

extension Double: Introduce {
    var infoString: String { "\(self)" }
}

기존 타입인 Int, Double에 Introduce 프로토콜을 채택하고 구현한 예제이다.

조건적으로 프로토콜 준수

where절을 사용하여 특정 조건을 만족할 때만 프로토콜을 채택하여 프로토콜에 명시된 프로퍼티, 메서드등을 사용하도록 할 수 있다

만약 Array의 원소(아이템)이 위에서 생성한 Introduce 프로토콜을 채택한 타입일 때만

Array에 Introduce 프로토콜을 채택하려면

extension Array: Introduce where Element: Introduce {
    var infoString: String {
        let itemString = self.map { $0.infoString }
        return itemString.joined(separator: ", ")
    }
}

let intArray = [1,2,3,4,5] // 사용 가능
let doubleArray = [1.2, 2.3, 4.5] // 사용 가능
let stringArray = ["sweet","dev"] // 사용 불가

Int, Double 타입의 Array는 Introduce 프로토콜을 채택하였기 때문에 Introduce 프로토콜을 채택하여

infoString을 사용할 수 있지만

String 타입은 Introduce 프로토콜을 채택하지 않았기 때문에 Introduce 프로토콜의 프로퍼티 infoString을 사용할 수 없다

확장을 통한 프로토콜 채택

타입이 프로토콜의 요구사항(프로퍼티, 메서드)을 구현하였지만 그 프로토콜을 아직 채택하지 않은 경우

즉 아래와 같은 경우

protocol Introduce {
    var infoString: String { get }
}

class SweetDev {
    var infoString: String { "Sweetfood-Dev is developer" }
}

SweetDev 타입은 Introduce 프로토콜의 요구사항인 infoString 프로퍼티를 구현하였지만

Introduce 프로토콜을 아직 채택하지 않았다.

이때 확장을 사용하여 프로토콜을 채택할 수 있다

extension SweetDev: Introduce { }

프로토콜의 요구사항을 구현하였다고 자동적으로 프로토콜을 채택하지 않는다 무조건 명시적으로 프로토콜을 선언해줘야함!

통합 구현을 사용한 프로토콜 채택

Swift는 많은 간단한 경우에 Equtable, Hashable, Comparable 프로토콜 구현을 자동으로 제공함

이러한 통합 구현은 프로토콜 요구사항을 직접 구현하기 위해 반복된 코드를 작성할 필요가 없게함

Equatable

다음과 같은 종류의 사용자 지정 타입에 Equatable 통합 구현을 자동으로 제공함

  • Equatable을 준수하는 저장 프로퍼티만 있는 구조체 타입
  • Equatable을 준수하는 연관값만 있는 열거형 타입
  • 연관값이 없는 열거형 타입
  1. 저장 프로퍼티만 있는 구조체 타입
struct Vector3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
}

let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
    print("These two vectors are also equivalent.")
}

Vector3D는 x, y, z는 모두 Equatable 프로토콜을 준수하는 Double 타입의 저장 프로퍼티만을

가지고 있는 구조체 타입이기 때문에 Equatable의 구현을 자동으로 제공 받음

  1. Equatable을 준수하는 연관값만 있는 열거형 타입
enum Job: Equatable {
    case student
    case developer(career: Int)
    case leave(years: Int)
}

var sweetfood = Job.developer(career: 5)
var another = Job.student

if sweetfood == another {
    print("equal")
}else {
    print("no equal")
}

연관 값이 없는 case 끼리 비교 시 같은 case면 같다

연관 값이 있는 다른 case끼리 비교 시에는 연관 값이 같더라도 같지 않다

var sweetfood = Job.developer(career: 5)
var another = Job.leave(years: 5)
// another와 sweetfood는 같지 않다

연관 값이 있는 같은 case일 경우 연관 값이 같아야 같다

var sweetfood = Job.developer(career: 5)
var another = Job.developer(career: 4)
// another와 sweetfood는 같지 않다
  1. 연관 값이 없는 열거형 타입
enum Job: Equatable {
    case student
    case developer
    case leave
}

let sweetfood = Job.developer
let another = Job.student

if sweetfood != another { print("different job") }

Hashable

다음과 같은 종류의 사용자 지정 타입에 Hashable 통합 구현을 자동으로 제공함

  • Hashable을 준수하는 저장 프로퍼티만 있는 구조체 타입
  • Hashable을 준수하는 연관값만 있는 열거형 타입
  • 연관값이 없는 열거형 타입

Comparable

다음과 같은 종류의 사용자 지정 타입에 Comparable 통합 구현을 자동으로 제공함

  • 원시 값(rawValue)가 없는 열거형 타입
  • Comparable을 준수하는 연관 값만 있는 열거형 타입
enum SkillLevel: Comparable {
    case beginner
    case intermediate
    case expert(stars: Int)
}
var levels = [SkillLevel.intermediate, SkillLevel.beginner,
              SkillLevel.expert(stars: 5), SkillLevel.expert(stars: 3)]
for level in levels.sorted() {
    print(level)
}
// Prints "beginner"
// Prints "intermediate"
// Prints "expert(stars: 3)"
// Prints "expert(stars: 5)"

댓글남기기