본문 바로가기
YAGOM CAREER STARTER

[토요스터디A반] 20230311: Protocol Oriented Programming

by Rhode 2023. 3. 13.

본문은 야곰 아카데미 커리어 스타터 캠프를 통해 학습한 내용을 회고한 글입니다.


Protocol Oriented Programming

STEP01

main과 Chargeable 소스파일을 만든 후 commit해준다. main.swift:

typealias WattPerHour = Int
typealias Watt = Int

Chargeable.swift:

protocol Chargeable {
    var maximumWattPerHour: WattPerHour { get set }
    
    func convert(chargeableWattPerHour: WattPerHour) -> WattPerHour
}

그리고 STEP01을 commit 해준다. commit 해주기 이전에 POPExercise.xcodeproj파일이 자꾸 commit 되려고 해서 fork에서 이 부분 역시 ignore 처리 해주었다:

 

STEP02

feat-MacBook, feat-charger라는 이름의 브랜치를 각각 생성해준다:

요구사항에는 Charger를 구조체로 구현하라고 되어있었지만, enum으로 한 번 구현해봤다. Charger 열거형의 코드:

enum Charger: Chargeable, Portable {
    case appleWatchCharger
    case iphoneFastCharger
    case ipadCharger
    case macBookCharger
    case macBookFastCharger
    
    var name: String {
        switch self {
        case .appleWatchCharger:
            return "5Wh 애플워치 충전기"
        case .iphoneFastCharger:
            return "18Wh 아이폰 고속 충전기"
        case .ipadCharger:
            return "30Wh 아이패드 충전기"
        case .macBookCharger:
            return "96Wh 맥북 충전기"
        case .macBookFastCharger:
            return "106Wh 맥북 충전기"
        }
    }
    
    var maximumWattPerHour: WattPerHour {
        switch self {
        case .appleWatchCharger:
            return 5
        case .iphoneFastCharger:
            return 18
        case .ipadCharger:
            return 30
        case .macBookCharger:
            return 96
        case .macBookFastCharger:
            return 106
        }
    }
    
    func convert(chargeableWattPerHour: WattPerHour) -> WattPerHour {
        return min(chargeableWattPerHour, maximumWattPerHour)
    }
}

MacBook 구조체의 코드:

import Foundation

struct MacBook: Portable {
    let name: String
    let maximumWattPerHour: WattPerHour
    var currentBattery: Watt {
        didSet {
            print("현재 저장된 배터리는 \(currentBattery)W입니다.")
        }
    }
    let maximumBattery: Watt
    
    init(name: String, maximumWattPerHour: Int, currentBattery: Int, maximumBattery: Int = 100) {
        self.name = name
        self.maximumWattPerHour = maximumWattPerHour
        self.currentBattery = currentBattery
        self.maximumBattery = maximumBattery
    }
    
    mutating func chargeBattery(charger: Chargeable) {
        print("\(name)을/를 충전합니다.")
        print("충전 중...")
        let chargeTime = calculateChargingTime(charger: charger)
        let formattedChargeTime = String(format: "%.2f", chargeTime)
        
        print("기존 저장된 배터리는 \(currentBattery)W였으며, \(charger.name)을 사용하여 완충까지 \(formattedChargeTime)시간 걸렸습니다.")
        
        currentBattery = maximumBattery
    }
    
    func calculateChargingTime(charger: Chargeable) -> Double {
        let batteryToBeCharged = maximumBattery - currentBattery
        let chargeTime = Double(batteryToBeCharged) / Double(charger.convert(chargeableWattPerHour: maximumWattPerHour))
        
        return chargeTime
    }
}

각각의 코드를 xcodeproj 파일 없이 stage에 올린 후 각각 commit해준다. 그리고 둘 다 commit이 되면 메인이 될 브랜치에 merge해준다.

 

step03

가방에 물건을 넣는 프로토콜을 채택해주면 된다

Portable프로토콜의 코드:

protocol Portable {
    var name: String { get }
}

struct Bag {
    private var items: [Portable] = [] {
        didSet {
            print("가방에 \(items.last?.name ?? "물건")을/를 넣었습니다.")
        }
    }
    
    mutating func put(item: Portable...) {
        item.forEach { item in
            items.append(item)
        }
    }
}

완성된 코드가 돌아가는 모습이다:

 

POP의 매커니즘

구체적인 타입이 아닌 추상화된 프로토콜을 통해 내부 구현을 숨긴다는 것이 이번 주차 학습의 핵심이었다고 볼 수 있다.

func calculateChargingTime(charger: Chargeable) -> Double {
    ...
}

위의 코드처럼 매개변수로 프로토콜을 받는 것이 그 사례라고 볼 수 있다.