假设我们有一个带有消息类型的简单枚举:
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
) - 不可能用假的
Audio
初始化MessageType.audio(Audio)
因为它可能有私有(private)初始化器 - 我希望避免
switch
、case let
或canHandle
中的其他硬编码检查
有什么建议吗?谢谢
最佳答案
如果用默认(或垃圾)值实例化枚举不是什么大问题
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 开始分解您的枚举。所以这是我的建议:依赖注入(inject)Handler
中的 Factory Pattern 的 em>。这可以非常干净地解决您的所有问题,而无需在 Handler 或 optionals 中触摸开关。
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
存储在您的类(class)中。您应该存储自己的类型,它是 MessageType
的某种映射版本,例如 DisassembledType
。
希望对您有所帮助!
注意事项
一些事情:
- 对不起,您的灵魂属于图书馆,真的。
- 使用适配器模式。 Clean C++是你的许多地方之一 可以了解一下。不要用其中之一污染您的整个代码库 他们的类型!这只是一个提示。
- 我知道你说过你不想使用开关......但你正在使用枚举......至少它在枚举内!
- 使用你自己的类型! (我说过了吗?)
- 使用适配器模式! (停下来。)
关于swift - 是否可以在数组中存储具有关联值的枚举模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49408946/