工厂构造函数应该如何在Swift中完成?

时间:2016-03-29 12:49:07

标签: swift factory

Swift开发人员似乎在说多态性应该通过协议来完成,而不是类继承。例如,假设你有一个Shapes数组,Shapes有不同的draw方法,Shape应该是一个协议而不是一个超类。

protocol Shape {
    func draw()
}

class Circle: Shape {
    func draw() { print("Drawing a circle") }
}

class Triangle: Shape {
    func draw() { print("Drawing a triangle") }
}

var shapes = [Shape]()

func fillShapes() {
    //... Add circles and triangles to shapes ...
}

// run
fillShapes()
for shape in shapes {
    shape.draw()
}

现在假设您要根据生成字符串的用户输入构建Shapes。你会如何以最Swift-y的方式编写一个接受String作为输入的Shape构造函数?

{p {3}}中的Cristik建议像这样的全局函数:

func buildShape(kind: String) -> Shape? {
    switch kind {
        case "Circle": return Circle()
        case "Triangle": return Triangle()
        default: return nil // Error - bad Shape
    }
}

这对我来说似乎很混乱。我宁愿将构造函数合并到Shape

所以我的第一次尝试是

extension Shape {
    static func build(kind: String) -> Shape? {
        switch kind {
            case "Circle": return Circle()
            case "Triangle": return Triangle()
            default: return nil // Error - bad Shape
        }
    }
}

但是调用Shape.build("Circle")!会产生error: static member 'build' cannot be used on instance of type 'Shape.Protocol'

我也试过

extension Shape {
    class Factory {
        func build(string: String) -> Shape? {
            switch kind {
                case "Circle": return Circle()
                case "Triangle": return Triangle()
                default: return nil
            }
        }
    }
    static let factory = Factory()
}

shapes.append(Shape.factory.build("Circle")!)

但它说无法在协议扩展中定义Factory。

Swift如何让我们制作灵活的构造函数?

4 个答案:

答案 0 :(得分:3)

协议不能(并且在我个人看来,不应该)用作“工厂”。

协议是构造遵循的一组规则,而不是具体的构造本身。工厂应该是具体类型,而不是“一套规则”。解决方案是使用单独的ShapeFactory构造(可以是classstructenum),具有静态/非静态build函数。 / p>

至于“凌乱”部分;我同意。这有点乱。 Swift不具备struct Shape.Factory { }之类的能力。如果没有妥协/重写编译器,就没有真正的方法可以绕过它,后者实际上可以通过swift-evolution邮件列表中的一些好的建设性批评来实现。

答案 1 :(得分:3)

你正在过度思考这个问题。没有什么比全球"更干净了。功能

某些语言中全局函数的问题在于它们正在污染全局命名空间,从而导致名称冲突。

但是,每个Swift项目都有自己的命名空间,它导入的每个框架也有自己的命名空间,因此名称冲突没有问题,因此全局函数没有任何问题。

不需要将简单函数包装到Factory类中只是为了创建命名空间的错觉(这将是Java中的正确解决方案,因为您无法在Java中创建全局函数)。

Shape.Factory.build(...)方式访问的功能并不比使用buildShape(...)访问的功能更好。

如果你真的想要一个命名空间,你可以将你的函数包装成struct

struct Shapes {
    static func buildShape(kind: String) -> Shape? {
        switch kind {
        case "Circle": return Circle()
        case "Triangle": return Triangle()
        default: return nil // Error - bad Shape
        }
    }
}

并将其称为Shapes.buildShape(...),但实际上并不需要它。

答案 2 :(得分:0)

我不会在Shape上使用Factory方法,协议和超类都不应该知道有关实现或派生类的详细信息。我宁愿选择ShapeBuilding协议和工厂类。

protocol Shape {
    func draw()
}

protocol ShapeBuilding {
    static func build(kind:String) -> Shape?
}

struct ShapeBuilder: ShapeBuilding {
    static func build(kind: String) -> Shape? {
        switch kind {
        case "Circle": return Circle()
        case "Triangle": return Triangle()
        default: return nil // Error - bad Shape
        }
    }
}
let circle = ShapeBuilder.build("Circle")

座右铭:仅仅因为Swift提供扩展,我们不必强制扩展所有内容。

答案 3 :(得分:0)

你们三个人都很好地了解了这个问题。我现在看到这是一个比我初想的更深刻的问题。

这是我的综合答案。

Swift消息避免构建抽象类层次结构。协议是胶水(或油脂可能?)而非抽象超类。

在Swift中,像Shape这样的协议不是Java意义上的Circle和Triangle的抽象超类。不要试图将工厂构造函数(可以返回多种类型的构造函数)放入协议中。

此帖子的上一版本建议shapes可以保留差异化的构造代码。但我改变了主意。 shapes也不应该知道圆圈和三角形。 shapes是多态的,这就是多态意味着什么。因此,我已经大大改变了这篇文章。如果有罢工,我会使用它,但我没有看到它。

我现在认为像这样的全球功能是正确的方式。

func buildShapeFrom(userInput: String) -> Shape? {
    switch userInput {
        case "Circle": return Circle()
        case "Triangle": return Triangle()
        default: return nil // Error - bad input
    }
}

如果用户输入端有适当的结构,也许可以去那里。用户关心它是圆形还是三角形,但Shape不应该shapes

欢迎评论。