[Swift] Protocols

Inwoo Hwang·2021년 8월 26일
0

Swift

목록 보기
2/8
post-thumbnail

Warning: 이해한 부분을 최대한 남기고 정리하려 남긴 글 입니다. 틀린 부분이 있을 수 있습니다. 이점 유의하고 읽어주시면 감사할 것 같습니다. 그리고 틀린 부분 알려주시면 바로바로 고치도록 하겠습니다.

Protocols

프로토콜이란?

협의된 규약, 규정?!?

스위프트의 규약, 규정 또는 약속!!

그리고 객체들이 소통하는 창구라 볼 수 있다

예시: 아이언맨이라고 규정한 우리의 약속

↓↓ 아래와 같은 특징을 가져야 아이언맨이라 부를 수 있다.

protocol IronMan {
    func fly()
    func call()
    func 비상탈출()
    
    var weapon: String { get }
    var color: UIColor { get }
}
struct MarkI: IronMan {
    func fly() {
        <#code#>
    }
    
    func call() {
        <#code#>
    }
    
    func 비상탈출() {
        print("씽씽 난다.")
    }
    
    var weapon: String
    
    var color: UIColor = .black
}

struct MarkII: IronMan {
    func fly() {
        <#code#>
    }
    
    func call() {
        <#code#>
    }
    
    func 비상탈출() {
        print("빨리빨리 난다.")
    }
    
    var weapon: String
    
    var color: UIColor = .red
}
  • 프로토콜은 특정 기능 수행에 필수적인 요소 를 정의하는 청사진[blueprint]이다.

  • 프로토콜을 만족시키는 타입[Type] 은 우리는 해당 타입이 프로토콜을 따른다[conform]고 말합니다.

  • 프로토콜에 필수 구현을 추가하거나 추가적인 기능을 더하기 위해 프로토콜을 확장[extend]하는 것이 가능합니다.

프로토콜은 왜 필요한가?

MarkI과 MarkII 모두에게 출동 명령을 내려야 하는데 이를 메서드로 표현하면...

	func 출동명령(누구에게: MarkI) {

}
	func 출동명령2(누구에게: MarkI) {
    
  }

위와 같이 MarkI 그리고 Mark II 둘 다에게 같은 명령의 메서드를 두 개나 불필요하게 만들어줘야 한다.

그런데 프로토콜을 사용하면 위와 같은 불필요한 상황을 피할 수 있습니다.

	func 출동명령(누구에게: IronMan) {
		
	}

let 마크1 = MarkI()
let 마크2 = MarkII()

출동명령(누구에게: 마크2)
출동명령(누구에게: 마크1)

위와 같이 메서드 하나만 생성한 뒤 MarkI과 MarkII가 채택한 프로토콜을 출동명령의 parameter 타입으로 지정 해 주면 해당 메서드를 마크1과 마크2 둘다 공유할 수 있습니다.

프로토콜의 정의

프로토콜의 정의는 클래스, 구조체, 열거형과 유사합니다.

protocol SomeProtocol {
    // protocol definition goes here
}

해당 프로토콜을 따르는 타입인 경우 아래와 같이 적습니다.

struct SomeStructure: SomeProtocol {
    // structure definition goes here
}

프로퍼티 요구사항

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
  	static var someTypeProperty: Int { get set }
}
  • 프로토콜에서는 저장된 프로퍼티인지 저장 프로퍼티인지 명시하지 않는다!

  • 명시해야 하는 것:

    • 프로퍼티의 이름

    • 프로퍼티의 타입

    • 그리고 해당 프로퍼티가 gettable한지 settable한지

    • 타입 프로퍼티는 static 키워드를 적어서 선언한다

protocol FullyNamed {
    var fullName: String { get }
}

class Starship: FullyNamed {
    var prefix: String?
    var name: String
    init(name: String, prefix: String? = nil) {
        self.name = name
        self.prefix = prefix
    }
    var fullName: String {
        return (prefix != nil ? prefix! + " " : "") + name
    }
}
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
// ncc1701.fullName is "USS Enterprise"

FullyNamed 는 하나의 프로퍼티를 갖고 있는 프로토콜이다.

이 프로토콜을 따르는 Starship이라는 구조체를 선언하면 프로토콜에 저장된 fullName 프로퍼티를 사용할 수 있고 위와 같이 computed property로 사용될 수 있다.

메소드 요구사항

메소드 파라미터의 기본 값은 프로토콜 안에서 사용할 수 없습니다.

protocol RandomNumberGenerator {    func random() -> Double}
  • 반환값을 지정할 수 있습니다.
  • 구현에 사용하는 괄호는 적지 않아도 됩니다.
class LinearCongruentialGenerator: RandomNumberGenerator {    var lastRandom = 42.0    let m = 139968.0    let a = 3877.0    let c = 29573.0    func random() -> Double {        lastRandom = ((lastRandom a + c).truncatingRemainder(dividingBy:m))        return lastRandom / m    }}let generator = LinearCongruentialGenerator()print("Here's a random number: \(generator.random())")// Prints "Here's a random number: 0.3746499199817101"print("And another one: \(generator.random())")// Prints "And another one: 0.729023776863283"

위 코드는 프로토콜의 필수 메소드 random() 을 구현한 클래스입니다.

MobileDevice타입을 Device protocol을 채택하여 구현하였습니다.

protocol Device {    var brand: String { get }    var name: String { get }        func turnOn()    func turnOff()}class MobileDevice: Device {    var brand: String    var name: String        init(brand: String, name: String) {        self.brand = brand        self.name = name    }    func turnOn() {        print("기기가 켜졌습니다")    }        func turnOff() {        print("기기가 꺼졌습니다.")    }}

변경 가능한 메소드 요구사항

protocol Togglable {    mutating func toggle()}enum OnOffSwitch: Togglable {    case off, on    mutating func toggle() {        switch self {        case .off:            self = .on        case .on:            self = .off        }    }}var lightSwitch = OnOffSwitch.offlightSwitch.toggle()// lightSwitch is now equal to .on

Togglable 프로토콜을 따르는 OnOffSwitch 에서 toggle() 메소드를 가져와서 해당 열거형의 내부를 변경할 수 있습니다.

Delegation

다시한 번 아이언맨 예시를 한 번 들어보자

protocol 비서능력 {    func 검색(무엇: String) -> String    func 운전(목적지: String)}protocol 비서능력 {    func 검색(무엇: String) -> String    func 운전(목적지: String)}struct 자비스: 비서능력 {    func 검색(무엇: String) -> String {        return "\(무엇)이 구글 검색 결과 입니다."    }        func 운전(목적지: String) {        print("운전합니다.")    }        }struct 포츠: 비서능력 {    func 검색(무엇: String) -> String {        return "\(무엇) 좀 그만 검색해요"    }        func 운전(목적지: String) {        print("운전시작")    }struct CEO {    var 비서: 비서능력?}

나의 권한과 할일을 위임 - Delegate

= Delegation Pattern

현실토니스타크가 어떤 이벤트가 일어났을 때 비서에게 일을 맡긴다는 것이 프로토콜이라는 협의체로 이뤄져있다. 비서공고를 할 떄 나는 특정한 능력이 필요하고 어떤 이벤트가 일어난 경우 어떻게 대응해야 하는지 이러한 요구을 적어 놓은 공고모집서를 현실세계 프로토콜의 delegation방식이 구현되어있다라고 볼 수 있다.

profile
james, the enthusiastic developer

0개의 댓글