2015-01-03 19 views
1

如何根据用户提供的实例确定协议是否符合特定的子类型,如果这种方式不可行,则可以使用任何替代解决方案。在运行时确定协议类型

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 

     } 
    } 
+0

为什么使用返回对象的函数数组而不是对象本身的数组? – lassej

+0

不知道我是否有问题,用户实际上会提供他们自己的Parent和Child类的实现,编辑 – user2727195

+0

是的,但函数可能是'func go(实例:[Super])',可能是calles用'go([Parent(),Child()])'。会更简单。 – lassej

回答

0

你需要从Objective-C的世界衍生做到这一点的对象:版本有不同的方法实现:

@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() 
    } 
} 
+0

谢谢你的尝试,但不想去那里的Objective-C世界,应该有一些我们可以在这里做的,顺便说一句,谢谢你帮助我改进问题 – user2727195

+0

本机Swift对象不' t还支持运行时检查。 – lassej

+0

任何解决方法,所有我能想到的是向孩子添加一个方法,说我是孩子,并且api首先调用这个方法,但是没有。 – user2727195

1

您正在跳过许多箍环来手动重新创建动态分派,即协议和类的目的之一。尝试使用真正的运行时多态性来解决您的问题。

把这个代码:

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,并从Type1Type2继承。或者,因为这里没有类继承,所以可以使用结构而不是类。

+0

实际上它不是关于调用重载时,它是父或孩子或超方法,它只是系统的行为不同,假设行为/模式是同步和异步,我编辑做一些东西/其他部分来详细说明 – user2727195

+0

编辑我当前的解决方法以及 – user2727195

+1

如果您尝试基于运行时值驱动行为,我建议尝试一个变量。你使用的类型是错误的。 –

1

几乎任何时候你发现自己想要使用is?,你可能打算使用枚举。枚举允许您使用相当于is?而不会感觉不好(因为它不是问题)。 is?是错误的OO设计的原因是它创建了一个关于子类型的函数,而OOP本身总是可以打开子类型(你应该将final看作编译器优化,而不是类型的基本部分)。

被子类型关闭不是问题或坏事。它只需要在功能范式而不是对象范例中进行思考。枚举(它是Swift类型的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?不是。