如何根据用户提供的实例确定协议是否符合特定的子类型,如果不可能采用这种方式,则确定任何替代解决方案。
API
protocol Super {}
protocol Sub: Super {} //inherited by Super protocol
class Type1: Super {} //conforms to super protocol
class Type2: Type1, Sub {} //conforms to sub protocol在另一个API类中
func store(closures: [() -> Super]) {
self.closures = closures
}是时候打电话了
func go() {
for closure in closures {
var instance = closure()
if instance is Super {
//do something - system will behave differently
} else { //it's Sub
//do something else - system will behave differently
}
}
}api的用户
class Imp1: Type1 {}
class Imp2: Type2 {}
var closures: [() -> Super] = [ { Imp1() }, { Imp2() } ]
store(closures)我当前在API中的解决方案
func go() {
for closure in closures {
var instance = closure()
var behavior = 0
if instance as? Type2 != nil { //not so cool, should be through protocols
behavior = 1 //instead of implementations
}
if behavior == 0 { //do something within the api,
} else { //do something else within the api
}
//instance overriden method will be called
//but not important here to show, polymorphism works in here
//more concerned how the api can do something different based on the types
}
}发布于 2015-01-03 14:04:54
您正在通过大量的循环手动重新创建动态调度,即协议和类的目的之一。实际使用真实的运行时多态性来解决您的问题。
取此代码:
if instance is Super {
//do something
} else { //it's Sub
//do something else
}您的意思是,如果是超类,运行超类方法,否则,运行子类。这有点颠倒--通常,当您是子类时,您希望运行子类代码,而不是相反。但是,假设您按照更常规的顺序将其转过来,那么您实际上是在描述调用协议的方法,并期望调用适当的实现:
(闭包实际上与当前的问题无关,所以暂时忽略它们)
protocol Super { func doThing() }
protocol Sub: Super { } // super is actually a bit redundant here
class Type1: Super {
func doThing() {
println("I did a super thing!")
}
}
class Type2: Sub {
func doThing() {
println("I did a sub thing!")
}
}
func doSomething(s: Super) {
s.doThing()
}
let c: [Super] = [Type1(), Type2()]
for t in c {
doSomething(t)
}
// prints “I did a super thing!”, then “I did a sub thing!"要考虑的其他选择:消除Sub,让Type2继承Type1。或者,由于这里没有类继承,所以可以使用structs而不是类。
发布于 2015-01-04 19:08:04
几乎任何时候,当您发现自己想使用is?时,您可能都会使用枚举。Enum允许您使用相当于is?的方法,而不必为此感到难过(因为它不是一个问题)。is?设计不好的原因是它创建了一个关闭子类型的函数,而OOP本身总是对子类型开放(您应该将final看作编译器优化,而不是类型的基本部分)。
不接受子类型不是问题,也不是坏事。它只需要以功能范式而不是对象范式来思考。Enum(它是求和类型的Swift实现)正是实现这个目的的工具,并且通常是一个比子类更好的工具。
enum Thing {
case Type1(... some data object(s) ...)
case Type2(... some data object(s) ...)
}现在在go()中,不是is?检查,而是switch。这不仅不是一件坏事,它是必需的,并由编译器进行全面的类型检查。
(示例删除了延迟闭包,因为它们实际上不是问题的一部分。)
func go(instances: [Thing]) {
for instance in instances {
switch instance {
case Type1(let ...) { ...Type1 behaviors... }
case Type2(let ...) { ...Type2 behaviors... }
}
}
}如果您有一些共享行为,只需将这些行为提取到函数中即可。您可以自由地让您的“数据对象”实现某些协议,或者具有特定的类,如果这样可以更容易地传递给共享函数。如果Type2接收的关联数据恰好是Type1的一个子类,那就好了。
如果您稍后加入并添加一个Type3,那么编译器将警告您注意没有考虑到这一点的每个switch。这就是为什么枚举是安全的,而is?则不安全。
发布于 2015-01-03 09:46:04
您需要从objects世界派生的对象才能做到这一点:
@objc protocol Super {}
@objc protocol Sub: Super {}
class Parent: NSObject, Super {}
class Child: NSObject, Sub {}
func go( closures: [() -> Super]) {
for closure in closures {
let instance = closure()
if instance is Sub { // check for Sub first, check for Super is always true
//do something
} else {
//do something else
}
}
}编辑:具有不同方法实现的版本:
protocol Super {
func doSomething()
}
protocol Sub: Super {}
class Parent: Super {
func doSomething() {
// do something
}
}
class Child: Sub {
func doSomething() {
// do something else
}
}
func go( closures: [() -> Super]) {
for closure in closures {
let instance = closure()
instance.doSomething()
}
}https://stackoverflow.com/questions/27753405
复制相似问题