代码之家  ›  专栏  ›  技术社区  ›  Leo

是否可以在数组中存储具有关联值的枚举模式?

  •  5
  • Leo  · 技术社区  · 6 年前

    假设我们有消息类型的简单枚举:

    enum MessageType {
        case audio
        case photo
        case text
    }
    

    Handler 类,该类仅处理特定类型的消息:

    class Handler {
        let allowed: [MessageType]
    
        init(_ allowed: [MessageType]) { self.allowed = allowed }
    
        func canHandle(_ messageType: MessageType) -> Bool {
            return allowed.contains(messageType)
        }
    }
    

    基本用法示例:

    let handler = Handler([.audio, .photo])
    print(handler.canHandle(.text)) // Prints false
    

    我想升级我的 MessageType 并为某些消息类型添加关联值。

    class Audio {}
    
    enum MessageType {
        case audio(Audio)
        case photo
        case text
    }
    

    问题是我无法在中存储枚举的模式 allowed 用于将来签入的阵列 canHandle :

    // error: '_' can only appear in a pattern or on the left side of an assignment
    let handler = Handler([.audio(_), .photo])
    

    是否有可能以“干净”的方式解决此类案件?

    • 无法修改 消息类型 因为它位于第三方库中(例如,使参数可选并传递 nil )
    • 无法初始化 MessageType.audio(Audio) 用假货 Audio 因为它可能有私人初始值设定项
    • 我希望避免 switch ,则, case let 或其他硬编码签入 CAN手柄

    有什么建议吗?谢谢

    do androids dream of electric enums

    1 回复  |  直到 6 年前
        1
  •  1
  •   Benjamin    6 年前

    如果用默认值(或垃圾值)实例化枚举不是什么大事

    enum MessageType {
        case audio(String)
        case photo
        case text
    }
    
    protocol SneakyEquatableMessage {
        func equals(message: MessageType) -> Bool
    }
    
    extension MessageType: SneakyEquatableMessage {
        func equals(message: MessageType) -> Bool {
            switch (self, message) {
            case (.audio(_), .audio(_)),
                 (.photo, .photo),
                 (.text, .text):
                return true
            default:
                return false
            }
        }
    }
    
    class Handler {
        let allowed: [MessageType]
    
        init(_ allowed: [MessageType]) { self.allowed = allowed }
    
        func canHandle(_ messageType: MessageType) -> Bool {
            return allowed.contains { $0.equals(message: messageType) }
        }
    }
    

    基本用途

    let handler = Handler([.audio(""), .photo])
    print(handler.canHandle(.text)) // Prints false
    print(handler.canHandle(.audio("abc")) //Prints true
    


    默认(或垃圾)值不现实

    在这种情况下,这一特定部分更为具体,但 最终,从Swift 4开始,您将以某种方式分解您的枚举。 所以我的建议是: 依赖项注入 工厂模式 在…内 Handler 。这非常干净地解决了您的所有问题,而无需触摸处理程序或选项中的开关。

    enum DisassembledMessage {
        case audio
        case photo
        case text
    }
    
    protocol MessageTypeFactory {
        func disassemble(message: MessageType) -> DisassembledMessage
        func disassemble(messages: [MessageType]) -> [DisassembledMessage]
    }
    
    class Handler {
        let allowed: [MessageType]
        let factory: MessageTypeFactory
    
        init(allowed: [MessageType], with factory: MessageTypeFactory) {
            self.allowed = allowed
            self.factory = factory
        }
    
        func canHandle(_ messageType: DisassembledMessage) -> Bool {
            return factory
                .disassemble(messages: allowed)
                .contains { $0 == messageType }
        }
    }
    

    基本用途

    let audioValue: Audio = //...
    let audioMessage = MessageType.audio(audioValue)
    let factory: MessageTypeFactory = //...
    let handler = Handler(allowed: [audioMessage, .photo], with: factory)
    print(handler.canHandle(.text)) // Prints false
    print(handler.canHandle(factory.disassemble(message: audioMessage))) //Prints true
    

    你可能会问:等等。。。您刚刚创建了另一个枚举(这只是一个示例,您可以将其转换为该协议中所需的任何内容)。我说:你使用的枚举来自一个库。。。请参见我的笔记部分。此外,您现在可以在任何需要将库类型分解为其他类型的地方使用该工厂,包括内部 处理程序 。你可以很容易地扩展协议 MessageTypeFactory 将枚举转换为您创建的其他类型(希望是行为),基本上只需在需要时与库类型保持距离即可。我希望这有助于澄清我的意思!我甚至不认为你应该 MessageType 在你的课堂上。您应该存储自己的类型,它是 消息类型 喜欢 DisassembledType

    我希望这有帮助!


    注释

    有几件事:

    • 真的很抱歉,你的灵魂属于图书馆。
    • 使用适配器模式。 Clean C++ 是你 可以了解它。不要用以下代码之一污染您的整个代码库 他们的类型!这只是一个提示。
    • 我知道你说过你不想用开关。。。但您正在使用枚举。。。至少它在枚举中!
    • 使用您自己的类型!(我是这么说的吗?)
    • 使用适配器模式!(停下来。)