Danny의 iOS 컨닝페이퍼
article thumbnail

[Swift/ TIL #34] 2023 / 07 / 06

Protocol의 요구사항들을 enum의 case로 적용시키는 방법 및

기본적으로 사용되는 enum에 Protocol을 채택하여 사용하는 방법을 알아봅시다.

 

 

처음 보는 선언 방식

열거형에 프로토콜을 채택하는 방법 중, 처음 보는 선언 방법이 있어 간단히 적어보려 합니다.

(Protocol의 요구사항들을 enum의 case로 적용시켜 나열시키는 방법입니다.)

 

설명은 주석을 참고해 주세요.

(제가 이해한 대로 적은 거라 틀린 내용이 있을 수 있습니다)

 

그리고 조금 더 자세한 정보를 알고 싶다면 밑에 참고 링크들을 참고해 주세요.

// Swift 5.3 에서 도입

protocol DataProtocol {
    // Self 자기 자신을 반환 (아래 Enum에서 프로토콜을 채택을 하면, MyEnum을 타입 자체를 리턴합니다)
    static var firstData: Self { get }
    static func secondData(extraData: String) -> Self
    
    var name: String { get }
}

enum MyEnum: DataProtocol {
    // DataProtocol 프로토콜을 채택했으므로, 타입 인스턴스(static)인 타입 프로퍼티 및 타입 메서드를 선언해줘야
    // 프로토콜의 요구사항을 충족하지만, 대신 enum의 각 case로도 대체가 가능합니다.
    
    // 이유를 설명하자면, enum의 각 case들은 정적인(static) 멤버로 간주됩니다.
    // 예를들면, MyEnum.firstData와 같이 사용하게 되면 열거형의 객체(인스턴스)가 생성되죠!?
    // 다시 표현을 한다면, '타입(MyEnum) + 타입 프로퍼티(firstData) == 객체 생성' 이런 느낌!
    // 즉, 프로토콜의 각 요구사항을 충족하므로 case로 대체가 가능해 집니다.
    case firstData
    case secondData(extraData: String)
    
    var name: String {
        switch self {
        case .firstData:
            return "First Data"
        case .secondData(let extraData):
            return "Second Data - extra Data: \(extraData)"
        }
    }
}
func loadData(_ data: DataProtocol) {
    print(data.name)
}

loadData(MyEnum.firstData)
loadData(MyEnum.secondData(extraData: "추가 데이터"))

// First Data
// Second Data - extra Data: 추가 데이터

 

장점

enum을 사용하므로 일관된 방식으로 데이터에 접근하여 사용할 수 있습니다.

또한, protocol을 사용하므로 다른 곳에 채택이 가능하므로 확장성이 있을 것 같습니다.

 

단점

protocol에 새로운 멤버가 추가될 때마다 enum의 소스를 수정해줘야 합니다.

그리고 많은 수의 case멤버가 있다면, name의 switch문의 크기가 엄청 커지게 됩니다.

또 다른 속성을 한 개 더 추가하면 2배로 swich문이 증가하겠죠...

 

흠, 여기서 개방/폐쇄 원칙이 깨지네요. 일단 많은 멤버를 사용해야 할 때는 struct으로 만들어 사용하는 걸로...

개방/폐쇄 원칙(Open/Closed Principle) - 확장에는 열려있고 기존 코드 변경에는 닫혀있어야 한다.

 

 

기본 사용 방법

이 방법은 클래스/구조체/열거형에서 프로토콜 채택 시 보통 사용되는 방식입니다.

(여기선 열거형으로 만들어 봅시다.)

protocol Animal {
    var name: String { get }
    func makeSound()
}

enum Dog: Animal {
    case shibe
    case poodle
    
    var name: String {
        switch self {
        case .shibe:
            return "시바"
        case .poodle:
            return "푸들"
        }
    }

    func makeSound() {
        print("왈!")
    }
}

enum Cat: Animal {
    case persian
    case siamese
    
    var name: String {
        switch self {
        case .persian:
            return "페르시안"
        case .siamese:
            return "샴"
        }
    }
    
    func makeSound() {
        print("야옹~")
    }
}
func myPet(_ animal: Animal) {
    print(animal.name)
    animal.makeSound()
}

myPet(Dog.shibe)
myPet(Cat.persian)

// 시바
// 왈!
// 페르시안
// 야옹~

 

장단점은 위 방법과 동일합니다!

 

 

참고

https://medium.com/codex/enums-cases-as-protocol

https://docs.swift.org/swift-book/documentation/the-swift-programming-language

https://github.com/apple/swift-evolution/blob/main/proposals/0280-enum-cases-as-protocol

 

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!