Enums 与静态工厂相比,何时使用具有关联值的枚举?

Enums 与静态工厂相比,何时使用具有关联值的枚举?,enums,swift,factory,Enums,Swift,Factory,在Swift中,您可以定义枚举并通过关联的值为其指定属性,例如: protocol SizeEnum { var length : Double? { get } // Length should be >= 0 - has to be an Optional for errors } enum SizesEnum : SizeEnum { case Short(length : Double) // 0 <= length <= maxShort c

在Swift中,您可以定义枚举并通过关联的值为其指定属性,例如:

protocol SizeEnum {
    var length : Double? { get } // Length should be >= 0 - has to be an Optional for errors
}

enum SizesEnum : SizeEnum {
    case Short(length : Double) // 0 <= length <= maxShort
    case Long(length : Double) // length > maxShort
    private static let maxShort =  1.0
    var length : Double? {
    get {
        switch self {
        case let .Short(length):
            if length >= 0 && length <= SizesEnum.maxShort { // Need to error check every access
                return length
            }
        case let .Long(length):
            if length > SizesEnum.maxShort { // Need to error check every access
                return length
            }
        }
        return nil // There was an error
    }
    }
}

SizesEnum.Short(length: 0.5).length // [Some 0.5]
SizesEnum.Short(length: 2).length // nil
SizesEnum.Long(length: 2).length // [Some 2.0]
SizesEnum.Long(length: -1).length // nil
这可能是
enum
的最佳示例,因为不需要进行错误检查,但即使如此,出厂版本仍具有竞争力。
可选的
示例非常简单,您甚至不需要工厂,只需直接创建一些。注意
None
如何不使用任何存储

条形码示例显示了工厂技术有多好;实际上,并非所有4个
Int
s的集合都是有效的UPCA,也并非所有
String
s都是有效的QR码,因此您需要进行错误检查,这对于
enum
s来说是一件痛苦的事情。以下是出厂版本:

class Barcode { // Note seperate storage for each case
    class UPCABarcode : Barcode {
        let type : Int, l : Int, r : Int, check : Int
        private init(type : Int, l : Int, r : Int, check : Int) {
            (self.type, self.l, self.r, self.check) = (type, l, r, check)
        }
    }
    class func UPCA(#type : Int, l : Int, r : Int, check : Int) -> UPCABarcode? {
        if ok(type: type, l: l, r: r, check: check) {
            return UPCABarcode(type: type, l: l, r: r, check: check)
        }
        return nil
    }
    class func QRCode(#s : String) -> Barcode? { // Have not expanded this case; use same pattern as UPCA
        return Barcode()
    }
    private init() {} // Prevent any other types of Barcode
    class func ok(#type : Int, l : Int, r : Int, check : Int) -> Bool {
        return true // In practice has to check supported type, range of L and R, and if check digit is correct
    }
}

Barcode.UPCA(type: 0, l: 1, r: 2, check: 3)
如果使用
enum
版本的
Barcode
,则每次使用
Barcode
时都必须检查其有效性,因为无法阻止无效的条形码。而工厂版本在创建时进行检查。请注意,
Barcode
没有存储,而
UPCA
有自定义存储。我没有编写
QRCode
,因为它使用了与
UPCA
相同的设计模式


我的印象是,
enum
版本在教程中看起来很棒,但在实践中很快就会因为错误处理而变得痛苦。

我相信最大的杀手级用例是
可选的
。它内置于语言中,但可选的是枚举:

enum Optional<T> {
    case None
    case Some(T)
}
对于结构,将有大量浪费的成员变量和一个混乱的接口来建模这类数据

我认为,如果对每种情况都使用相同的类型,那么对枚举使用关联值是没有意义的。在这种情况下,成员变量更干净。关联值的重点是更准确地建模某些类型的数据。最有用的情况是类型的不同实例可以有不同的关联数据。这可能是通过子类实现的,但是需要向下转换才能访问更多特定的变量,并且声明将更加详细。枚举是表示这类数据的简洁方法

另一个例子是web请求:

struct NetRequest {
    enum Method {
        case GET
        case POST(String)
    }

    var URL: String
    var method: Method
}

var getRequest = NetRequest(URL: "http://drewag.me", method: .GET)
var postRequest = NetRequest(URL: "http://drewag.me", method: .POST("{\"username\": \"drewag\"}"))

当我想到“enum”时,我一点也不想到“工厂”。通常,工厂用于更大更复杂的类结构。枚举应该是非常小的数据块,没有什么逻辑。

谢谢你的评论,我编辑了原文以给出回应,因为我想展示我在评论中做不到的代码。@HowardLovatt,你实现可选的例子比枚举要复杂得多,我认为你太依赖Java模式了。为什么不在枚举上使用
validate()->Bool
方法呢?我认为枚举对您来说似乎很痛苦,因为您希望使用现有的设计模式。老实说,从一眼就能理解工厂模式的角度来看,你给出的每一个工厂模式的例子对我来说都是难以置信的痛苦。为无效对象返回
nil
不如使用
validate
方法清晰。不过我相信这是个人的偏好
enum Optional<T> {
    case None
    case Some(T)
}
enum Barcode {
    case UPCA(Int, Int, Int, Int)
    case QRCode(String)
}
struct NetRequest {
    enum Method {
        case GET
        case POST(String)
    }

    var URL: String
    var method: Method
}

var getRequest = NetRequest(URL: "http://drewag.me", method: .GET)
var postRequest = NetRequest(URL: "http://drewag.me", method: .POST("{\"username\": \"drewag\"}"))