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

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])

是否有可能以“干净”的方式解决这种情况?
  • 无法修改MessageType,因为它在第三方库中(例如,使参数成为可选参数并传递nil)
  • 无法使用伪造的MessageType.audio(Audio)初始化Audio,因为它可能具有私有(private)初始化程序
  • 我希望避免switch
  • 中的case letcanHandle或其他硬编码检查

    有什么建议么?谢谢

    swift - 是否可以将带有关联值的枚举模式存储在数组中?-LMLPHP

    最佳答案

    如果用默认(或无用值)实例化枚举并不重要

    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内的工厂模式的依赖注入(inject)。这样就可以非常干净地解决您的所有问题,而无需触摸Handler或Optional中的开关。
    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
    

    您可能会问:等一下...您刚刚创建了另一个枚举(这只是一个示例,您可以将其转换为该协议(protocol)中想要的任何名称)。好吧,我说:您使用的枚举来自库...请参阅我的注释部分。而且,您现在可以在需要将库类型分解为某种类型的任何地方使用该工厂,包括Handler内部。您可以轻松地扩展协议(protocol)MessageTypeFactory,以将您的枚举转换为您创建的其他类型(希望是行为),并且基本上只是在需要时与库类型保持距离。我希望这有助于弄清我的意思!我什至不认为您应该在类中存储MessageType。您应该存储自己的类型,这是MessageType的某种映射版本,例如DisassembledType

    我希望这有帮助!

    注释

    一些东西:
  • 很抱歉,您的灵魂归图书馆所有。
  • 使用适配器模式。 Clean C++是您许多地方之一
    可以了解它。不要使用以下任何一种方式污染您的整个代码库
    他们的类型!这只是一个提示。
  • 我知道您说过您不想使用开关...但是您在使用枚举...至少在枚举之内!
  • 使用您自己的类型! (我说过吗?)
  • 使用适配器模式! (停止)。
  • 关于swift - 是否可以将带有关联值的枚举模式存储在数组中?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49408946/

    10-12 14:12
    查看更多