2015-01-03 100 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?不是。