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如何让我们制作灵活的构造函数?
答案 0 :(得分:3)
协议不能(并且在我个人看来,不应该)用作“工厂”。
协议是构造遵循的一组规则,而不是具体的构造本身。工厂应该是具体类型,而不是“一套规则”。解决方案是使用单独的ShapeFactory
构造(可以是class
,struct
或enum
),具有静态/非静态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
。
欢迎评论。