[英]Swift opposite to “required”, or factory design pattern with required init methods in superclass
[英]Swift: Factory Pattern, Subclass methods are not visible
我是Swift编程的新手,在构造Factory设计模式时遇到了障碍。 下面是我的代码:
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
,它mgr.SubClassMethod
引发错误提示
IInterface类型的值没有成员SubClassMethod
我相信尽管通过工厂返回了SubClass对象,但我只能使用协议方法
我浏览并看到的所有示例仅显示了如何使用协议指定的方法,但是我没有看到一个示例,该示例显示了如何使用除协议方法之外的子类自己的方法
您错过了工厂模式的要点。 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()
}
}
调用Factory方法的客户端代码不应推测其返回值,并尝试将其强制转换为具体类。 这完全破坏了使用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()
}
}
实际上,存在三种模式- 工厂 , 抽象工厂和工厂方法 。 您可以在此处了解差异。
协议可以由几个实体符合 (在你的情况下,类SubClass
),但协议本身并不知道这所符合它的实体。 由于您的createFactory()
方法返回的是类型(协议)的IInterface
而不是类型的SubClass
,因此即使将SubClass
对象作为返回值发送,按原样返回也不会知道特定于SubClass
成员。
let mgr = Factory.createFactory() /* Type: let mgr: IInterface */
当尝试在实例mgr
上调用成员SubClassMethod
(或该SubClassMethod
任何未知成员,例如.foo
,也很明显
错误:类型为“ IInterface”的值没有成员“ SubClassMethod”
总结起来,符合协议的类型将可以访问该协议中的所有蓝图方法和属性,但是可以将协议的实例用作类型(如果您在内部没有Self或任何关联的类型,则可以接受您的协议)对碰巧符合该协议的其他类型的方法和属性一无所知。
如下所述,如果您知道IInterface
类型的返回IInterface
是某种类型,则可以尝试将类型转换( as?
)转换为该类型,例如... as? SubClass
... as? SubClass
。 请再次注意,协议IInterface
不知道符合该协议的类型,因此不能断言这种类型转换在编译时是成功的。 严格来说,这是由您作为开发人员来控制的,如果您不小心,可能会导致运行时异常(例如,使用不安全的方法,如强制转换as!
)。 现在,如果函数始终返回SubClass
类型,则最好将其签名更改为
class func createFactory() -> SubClass {
保留IInterface
返回的可能用例是,如果createFactory()
实际上确实返回了均符合IInterface
协议的不同类型 。 在这种情况下,您可以安全地在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
为返回:
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问题的答案可能对您有价值
您的let mgr
的类型为IInterface
,实际上没有SubClassMethod
。
如果您知道Factory.createFactory()
返回SubClass
,则可以像这样将其SubClass
为SubClass
:
let mgr = Factory.createFactory() as! SubClass
然后let mgr
成为SubClass
类型,您应该可以调用mgr.SubClassMethod
。
但是,即使您将类命名为Factory,您的示例也与工厂设计模式无关。 查看@mixel的答案以了解详细信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.