Swift: 工厂模式,子类方法不可见

Swift: Factory Pattern, Subclass methods are not visible

我是 Swift 的编程新手,在构建工厂设计模式时遇到了障碍。下面是我的代码:

protocol IInterface {
   func InterfaceMethod() -> Void
}

public class SubClass: IInterface {
   init() {}

   func InterfaceMethod() { }

   public func SubClassMethod() { }
}

public class Factory() {
    class func createFactory() -> IInterface {
         return SubClass()
    }
}

最后,我尝试像下面那样访问它

let mgr = Factory.createFactory()

//calling Interface Method
mgr.InterfaceMethod() -- This works, when i keep a dot (mgr.) it shows the method name
//calling subclass method
mgr.SubClassMethod() -- This doesn't work, when i keep a dot (mgr.) it doesnt even show the subclass method name

即使我使用 mgr.SubClassMethod,它也会抛出错误

Value of type IInterface has no member SubClassMethod

虽然通过工厂返回了 SubClass 对象,但我相信我只暴露了协议方法

我浏览和看到的所有例子都只展示了如何使用协议指定的方法,但我没有看到一个例子说明如何使用协议方法之外的子类自己的方法

您的 let mgr 属于 IInterface 类型,但确实没有 SubClassMethod.

如果你知道 Factory.createFactory() returns SubClass 你可以像这样把它转换成 SubClass:

let mgr = Factory.createFactory() as! SubClass

那么 let mgr 将是 SubClass 类型,您应该可以调用 mgr.SubClassMethod.

尽管如此,您的示例与工厂设计模式无关,即使您命名为 class Factory。详情请看@mixel 的回答。

一个协议可以被几个实体遵守(在你的例子中,class SubClass),但是协议本身并不知道哪些实体符合。由于您的 createFactory() 方法 return 是一种类型(协议)IInterface,而不是类型 SubClass,因此 return 不会知道特定于 [=14 的成员=],即使 SubClass 对象作为 return 发送。

let mgr = Factory.createFactory() /* Type: let mgr: IInterface */

mgr

实例中尝试调用成员 SubClassMethod(或任何未知成员,例如 .foo 时,这一点也很明显)

error: value of type 'IInterface' has no member 'SubClassMethod'

总而言之,符合协议的类型将有权访问该协议中的所有蓝图方法和属性,但是用作类型的协议实例(如果您没有 Self 则可以接受或您的协议中的任何关联类型)将对恰好符合该协议的其他类型的方法和属性一无所知。


如下所述,如果知道类型IInterface的return是某种类型,那么你可以尝试类型转换(as?) 到这个类型,例如... as? SubClass。但是请再次注意,协议 IInterface 不知道哪些类型符合它,因此这种类型转换不能在编译时断言为成功;作为开发人员,这是严格由您控制的事情,如果您不小心(例如使用 un-safe 方法,例如强制转换 as!),可能会导致运行时异常。现在,如果函数总是 return 是 SubClass 类型,您不妨将其签名更改为

class func createFactory() -> SubClass {

保留 IInterface return 的可能用例是 createFactory() 实际上 return 不同类型 所有符合 IInterface 协议。在这种情况下,您可以安全地在 createFactory() return 上使用 switch 块对不同的 known 类型(开发人员已知)执行类型转换) 符合IInterface,如下

protocol IInterface {
    func InterfaceMethod() -> Void
}

public class SubClass: IInterface {
    init() {}

    func InterfaceMethod() { }

    public func SubClassMethod() { }
}

public class AnotherClass: IInterface {
    init() {}

    func InterfaceMethod() { }

    public func AnotherClassMethod() { }
}

public class Factory {
    class func createFactory() -> IInterface {

        if arc4random_uniform(5) > 2 {
            return SubClass()
        }
        else {
            return AnotherClass()
        }
    }
}

switch 在调用你的工厂方法时阻塞 return:

switch(Factory.createFactory()) {
case let mgr as SubClass:
    print("Subclass")
    mgr.SubClassMethod()
case let mgr as AnotherClass:
    print("AnotherClass")
    mgr.AnotherClassMethod()
// default case
case let mgr as IInterface:
    print("Unknown specific regarding return type")
}

最后,以下 SO 问题的答案可能对您有用

  • Returning constrained generics from functions and methods

你错过了工厂模式的重点。 工厂模式的思想是提供具有特定 return 类型的方法和具有此类型或继承自此类型(如果它是 class 类型)或符合此类型的 return 实例(如果它是 protocol)。

protocol Animal {
    func voice() -> String
}

class Dog: Animal {
    func voice() -> String {
        return "bark"
    }

    func sit() {
    }
}

class Cat: Animal {
    func voice() -> String {
        return "meow"
    }
}

class AnimalFactory {
    func getAnimal() -> Animal {
        return Dog()
    }
}

调用工厂方法的客户端代码不应推测其 return 值并尝试将其转换为具体的 class。这完全破坏了使用工厂模式的意义。

正如您在上面的示例中所见,AnimalFactory.getAnimal() return 某种类型的实例符合 Animal 协议。调用此方法的代码不知道也不应该知道该实例的特定类型。

如果调用工厂方法的代码期望 returning 实例具有类型 Dog 或继承自此类型,那么您应该创建并使用单独的 DogFactory:

class EvilDog: Dog {
    override func voice() -> String {
        return "bark-bark"
    }
}

class DogFactory {
    func getDog() -> Dog {
        return EvilDog()
    }
}

您可能会遇到这样的情况:客户端代码根据由工厂方法 return 编辑的真实实例类型实现不同的行为。在这种情况下 AnimalFactory 应该实施方法来提供将在客户端代码中使用的所有类型的实例:

class AnimalFactory {
    func getDog() -> Dog {
        return EvilDog()
    }

    func getCat() -> Cat {
        return Cat()
    }
}

func trainAnimal(iLikeDogs: Bool, animalFactory: AnimalFactory) {
    if iLikeDogs {
        let dog = animalFactory.getDog()
        dog.voice()
        dog.sit() // only dog can sit
    } else {
        let cat = animalFactory.getCat()
        cat.voice()
    }
}

实际上有三种模式——工厂抽象工厂工厂方法。您可以阅读差异 here.