简体   繁体   English

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

[英]Swift: Factory Pattern, Subclass methods are not visible

I am new to programming in Swift and I have reached a blocker in constructing a Factory design pattern. 我是Swift编程的新手,在构造Factory设计模式时遇到了障碍。 Below is my code: 下面是我的代码:

protocol IInterface {
   func InterfaceMethod() -> Void
}

public class SubClass: IInterface {
   init() {}

   func InterfaceMethod() { }

   public func SubClassMethod() { }
}

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

Finally, i am trying to access it like below 最后,我正在尝试像下面这样访问它

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

Even if i use mgr.SubClassMethod , it throws error saying 即使我使用mgr.SubClassMethod ,它mgr.SubClassMethod引发错误提示

Value of type IInterface has no member SubClassMethod IInterface类型的值没有成员SubClassMethod

I believe i am getting exposed only with protocol methods though a SubClass object is returned via factory 我相信尽管通过工厂返回了SubClass对象,但我只能使用协议方法

All the examples I browsed and have seen shows only how to consume methods specified by the protocol, but i haven't seen an example where it shows how to consume a subclass's own methods apart from protocols methods 我浏览并看到的所有示例仅显示了如何使用协议指定的方法,但是我没有看到一个示例,该示例显示了如何使用除协议方法之外的子类自己的方法

You miss the point of Factory pattern. 您错过了工厂模式的要点。 The idea of Factory pattern is to provide methods that have specific return type and return instances that either has this type or inherits from this type (if it's class type) or conforms to this type (if it's protocol ). Factory模式的想法是提供具有特定返回类型的方法,并提供具有该类型或从该类型继承(如果是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()
    }
}

Client code calling Factory method should not speculate about its return value and try to cast it to concrete class. 调用Factory方法的客户端代码不应推测其返回值,并尝试将其强制转换为具体类。 That's totally ruining the point of using Factory pattern. 这完全破坏了使用Factory模式的意义。

As you can see in above example AnimalFactory.getAnimal() return instance of some type that conforms to Animal protocol. 如您在上面的示例中看到的, AnimalFactory.getAnimal()返回符合Animal协议的某种类型的实例。 Code calling this method does not know and should not know about specific type of that instance. 调用此方法的代码不知道也不应该知道该实例的特定类型。

If code calling Factory method expects that returning instance has type Dog or inherits from this type then you should create and use separate DogFactory : 如果调用Factory方法的代码期望返回的实例具有Dog类型或从该类型继承,则您应该创建并使用单独的DogFactory

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

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

You can have situation when client code implement different behavior dependent on real type of instance that is returned by factory method. 当客户端代码根据工厂方法返回的实例的实际类型实现不同的行为时,您可能会遇到这种情况。 In that case AnimalFactory should implement methods for providing instances of all types that will be used in client code: 在那种情况下, 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()
    }
}

Actually there are three patterns - Factory , Abstract Factory and Factory method . 实际上,存在三种模式- 工厂抽象工厂工厂方法 You can read about differences here . 您可以在此处了解差异。

A protocol can be conformed to by several entities (in your case, class SubClass ), but the protocol itself doesn't know about which entities that are conforming to it . 协议可以几个实体符合 (在你的情况下,类SubClass ),但协议本身并不知道这所符合它的实体。 Since your createFactory() method returns a type (protocol) IInterface , not type SubClass , the return as is will not know about members specific to SubClass , even if a SubClass object is sent as return. 由于您的createFactory()方法返回的是类型(协议)的IInterface而不是类型的SubClass ,因此即使将SubClass对象作为返回值发送,按原样返回也不会知道特定于SubClass成员。

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

This is also apparent when attempting to call a member SubClassMethod (or any unknown member, say .foo , for that metter) on instance mgr 当尝试在实例mgr上调用成员SubClassMethod (或该SubClassMethod任何未知成员,例如.foo ,也很明显

error: value of type 'IInterface' has no member 'SubClassMethod' 错误:类型为“ IInterface”的值没有成员“ SubClassMethod”

To wrap it up, a type conforming to a protocol will have access to all blueprinted methods and properties in that protocol, but an instance of a protocol used as a type (which is acceptable if you don't have Self or any associated types in your protocol) will know nothing about the methods and properties of other types that happens to conforms to that protocol. 总结起来,符合协议的类型将可以访问该协议中的所有蓝图方法和属性,但是可以将协议的实例用作类型(如果您在内部没有Self或任何关联的类型,则可以接受您的协议)对碰巧符合该协议的其他类型的方法和属性一无所知。


As mentioned below, if you know the return of type IInterface is of a certain type, then you can attempt a type conversion ( as? ) to this type, eg ... as? SubClass 如下所述,如果知道IInterface类型的返回IInterface是某种类型,则可以尝试将类型转换( as? )转换为该类型,例如... as? SubClass ... as? SubClass . ... as? SubClass Note however again that the protocol IInterface doesn't know about which types that conforms to it, to this type conversion can't be asserted as successful in compile time; 请再次注意,协议IInterface不知道符合该协议的类型,因此不能断言这种类型转换在编译时是成功的。 this is strictly something to be controlled by you as developer, possibly leading to runtime exceptions if you're not careful (eg using un-safe methods such as forced conversion as! ). 严格来说,这是由您作为开发人员来控制的,如果您不小心,可能会导致运行时异常(例如,使用不安全的方法,如强制转换as! )。 Now, if the function always returns a SubClass type, you might as well change its signature to 现在,如果函数始终返回SubClass类型,则最好将其签名更改为

class func createFactory() -> SubClass {

As possible use case of keeping the IInterface return is if createFactory() actually do return different types that all conform to the IInterface protocol. 保留IInterface返回的可能用例是,如果createFactory()实际上确实返回了均符合IInterface协议的不同类型 In that case, you could safely use a switch block over the createFactory() return to perform type conversion to you different known types (known by developer) that conform to IInterface , as follows 在这种情况下,您可以安全地在createFactory()返回值上使用switch块,以对符合IInterface不同已知类型(开发人员已知 createFactory()执行类型转换,如下所示

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 block over return when calling you factory method: 调用工厂方法时将块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")
}

Finally, possibly the answer to the following SO question can be of value for you 最后,以下SO问题的答案可能对您有价值

Your let mgr is of type IInterface that indeed does not have SubClassMethod . 您的let mgr的类型为IInterface ,实际上没有SubClassMethod

If you know that Factory.createFactory() returns SubClass you can cast it to SubClass like that: 如果您知道Factory.createFactory()返回SubClass ,则可以像这样将其SubClassSubClass

let mgr = Factory.createFactory() as! SubClass

Then let mgr will be of type SubClass and you should be able to call mgr.SubClassMethod . 然后let mgr成为SubClass类型,您应该可以调用mgr.SubClassMethod

Nevertheless, your example has nothing to do with factory design pattern even though you named class Factory. 但是,即使您将类命名为Factory,您的示例也与工厂设计模式无关。 Take a look at @mixel's answer for details. 查看@mixel的答案以了解详细信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM