【问题标题】:Swift: Factory Pattern, Subclass methods are not visibleSwift:工厂模式,子类方法不可见
【发布时间】:2016-01-16 10:18:59
【问题描述】:

我是 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,它也会抛出错误提示

IInterface 类型的值没有成员 SubClassMethod

我相信我只会暴露在协议方法中,尽管通过工厂返回子类对象

我浏览和看到的所有示例都只显示了如何使用协议指定的方法,但我还没有看到显示如何使用子类自己的方法而不是协议方法的示例

【问题讨论】:

    标签: ios swift factory factory-pattern


    【解决方案1】:

    您错过了工厂模式的要点。 工厂模式的思想是提供具有特定返回类型的方法,并返回具有该类型或继承自该类型(如果是class 类型)或符合该类型(如果是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()
        }
    }
    

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

    正如您在上面的示例中看到的那样,AnimalFactory.getAnimal() 返回符合Animal 协议的某种类型的实例。调用此方法的代码不知道也不应该知道该实例的具体类型。

    如果调用 Factory 方法的代码期望返回实例的类型为 Dog 或继承自该类型,那么您应该创建并使用单独的 DogFactory

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

    当客户端代码根据工厂方法返回的实例的真实类型实现不同的行为时,您可能会遇到这种情况。在这种情况下 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()
        }
    }
    

    其实有三种模式——FactoryAbstract FactoryFactory method。您可以阅读有关差异here

    【讨论】:

    • 这个答案的核心问题是EvilDog狗也回复.sit(),这不是可能的行为。可能将var obedient : Bool = true 属性添加到Dog,并为EvilDog 翻转到false,然后修改.sit() 函数以返回一个布尔值,即此obedient 属性。除了笑话,好帖子,+1! ;)
    【解决方案2】:

    一个协议可以符合几个实体(在你的例子中,类SubClass),但协议本身不知道哪些实体符合它 /em>。由于您的createFactory() 方法返回类型(协议)IInterface,而不是类型SubClass,因此返回原样不会知道特定于SubClass 的成员,即使SubClass 对象作为返回发送。

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

    这在尝试调用实例 mgr 上的成员 SubClassMethod(或任何未知成员,例如 .foo)时也很明显 mgr

    错误:“IInterface”类型的值没有成员“SubClassMethod”

    总而言之,符合协议的类型将可以访问该协议中的所有蓝图方法和属性,但是用作类型的协议实例(如果您没有 Self 或任何协议中的关联类型)将不知道碰巧符合该协议的其他类型的方法和属性。


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

    class func createFactory() -> SubClass {
    

    保持IInterface 返回的可能用例是如果createFactory() 实际上确实返回所有符合IInterface 协议的不同类型。在这种情况下,您可以安全地在createFactory() 返回上使用switch 块来执行类型转换为符合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 在调用工厂方法时阻塞返回:

    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 问题的答案可能对您有价值

    【讨论】:

      【解决方案3】:

      您的let mgrIInterface 类型,实际上没有SubClassMethod

      如果您知道Factory.createFactory() 返回SubClass,您可以像这样将其转换为SubClass

      let mgr = Factory.createFactory() as! SubClass
      

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

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

      【讨论】:

      • 你的回答与工厂模式的概念相矛盾。
      • @mixel 你是对的,但 OP 没有明确指定问题,我的回答至少有助于编译他的例子。希望添加的解释更清楚。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-30
      • 1970-01-01
      相关资源
      最近更新 更多