2015-01-29 9 views
6

के लिए स्विफ्ट प्रतिनिधि प्रोटोकॉल मेरे पास एक कक्षा है, StateMachine, जो कि राज्यों के विभिन्न सेटों को लागू करने के लिए जेनेरिक है, उदाहरण के लिए, एक enum। जब राज्य मशीन एक नई स्थिति में प्रवेश करती है तो मैं एक प्रतिनिधि को सूचित करने के लिए StateMachineDelegate प्रोटोकॉल का उपयोग करना चाहता हूं।जेनेरिक क्लास

लेकिन यह काम नहीं करता है क्योंकि प्रतिनिधि प्रोटोकॉल भी प्रकार की आवश्यकताओं के साथ सामान्य है। त्रुटि दिखाती है कि delegate संपत्ति घोषित की गई है।

protocol StateType: Hashable {} 

protocol StateMachineDelegate: class { 
    typealias S: StateType 
    func stateMachine(stateMachine: StateMachine<S>, didEnterState newState: S) 
} 

class StateMachine<S: StateType> { 
    typealias State = S 

    weak var delegate: StateMachineDelegate? 
    //~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~ 
    //Protocol 'StateMachineDelegate' can only be used as a generic constraint because it has Self or associated type requirements 

    var currentState: State {...} 

    init(initialState: State) {...} 

    func applyState(toState: State) -> Bool { 
     ... 
     currentState = toState 
     delegate?.stateMachine(self, didEnterState: toState) 
     ... 
    } 
} 



मैं किसी भी तरह StateMachine कक्षा में है कि StateMachineDelegate.S == S संबद्ध करने की आवश्यकता है, लेकिन मैं ऐसा करने के तरीके यकीन नहीं है, या अगर यह संभव है। मैंने कोशिश की:

class StateMachine<S: StateType, D: StateMachineDelegate where D.S == S> { 
    ... 
    weak var delegate: D? 
    ... 
} 

लेकिन फिर मैं प्रोटोकॉल rework करने के लिए ठीक से StateMachine के जेनेरिक प्रकार की घोषणा करने की कोशिश कर अटक जाते हैं। और StateMachine बनाते समय प्रतिनिधि के सामने के प्रकार की घोषणा करना सही नहीं लगता है।

उत्तर

0

मुझे लगता है कि इसकी सिर्फ एक नाम टक्कर समस्या ... इस प्रयास करें:

protocol StateType: Hashable {} 

protocol StateMachineDelegate: class { 
    typealias State: StateType 
    func stateMachine(stateMachine: StateMachine<State>, didEnterState newState: State) 
} 

class StateMachine<S: StateType> { 
    typealias State = S 

    weak var delegate: StateMachineDelegate? 


    var currentState: State {...} 

    init(initialState: State) {...} 

    func applyState(toState: State) -> Bool { 
     ... 
      currentState = toState 
     delegate?.stateMachine(self, didEnterState: toState) 
     ... 
    } 
} 

आप घोषित करने के लिए क्या सामान्य प्रकार प्रोटोकॉल में परिभाषित के नाम वर्ग यह के अनुरूप में होना चाहिए की जरूरत है। यदि इस समाधान आपकी आवश्यकताओं के लिए ठीक है

+1

वैसे, यह देखते हुए कि राज्य मशीन बहुत सारी समस्याओं के लिए एक अच्छा डिजाइन पैटर्न है, क्यों राज्य को परेशान करने के लिए बाध्यकारी है? आप संबंधित मूल्यों के साथ enums की शक्ति खो देते हैं (या कम से कम आपको उन्हें हर्षनीय रूप से अनुरूप बनाना होगा)। संबंधित मशीनों के साथ आईएमएचओ एनम्स राज्य मशीनों के तर्क के लिए महान हैं। –

+0

धन्यवाद, लेकिन यह काम नहीं करता है। मुझे लगता है कि समस्या थोड़ा गहराई से चलती है। मेरा अनुमान है कि संकलक (किसी भी कारण से) गारंटी नहीं दे सकता कि 'स्टेटमाचिन डिलेगेट' की प्रकार की आवश्यकताएं पूरी होंगी, क्योंकि 'स्टेटमाचिन' उस संबंध में कोई वादा नहीं करता है। यदि 'स्टेटमैचिन' में 'स्टेटमैचिन डिलेगेट' को बाध्य एक _generic_ प्रकार पैरामीटर शामिल है, तो यह वादा करता है कि इसे रनटाइम पर एक संतोषजनक वर्ग के साथ प्रतिस्थापित किया जाएगा (लेकिन फिर मैं प्रतिनिधि प्रोटोकॉल में 'स्टेटमाचिन ' के अजीब पैरामीटर के साथ समाप्त होता हूं) । शायद भाषा विकसित होने के साथ ही यह संभव हो जाएगा। – Stuart

+0

हैशबल बाधा के बारे में, मैंने ऐसा इसलिए किया क्योंकि राज्य मशीन वास्तव में राज्यों और राज्य संक्रमणों को संग्रहित करती है, और यह निर्धारित करने के लिए कि उनके अनुरोधित राज्य परिवर्तन मान्य है या नहीं, उनके अस्तित्व के लिए जांच करता है। इस प्रश्न में दिखाए गए वर्ग की तुलना में थोड़ा अधिक जटिल है, और मैं और अधिक जटिल हो जाऊंगा क्योंकि मैं आगे प्रयोग करता हूं! यदि संबंधित enums आसान हैं, तो उन्हें सुझाव देने के लिए उन्हें 'हैशबल' के अनुरूप करने में कोई समस्या नहीं होनी चाहिए। – Stuart

1

देखें, यह @autoclosure का उपयोग करता पुनरावर्ती सामान्य परिभाषा के साथ एक समस्या से छुटकारा पाने के:

class StateMachine<S: Printable, D: StateMachineDelegate where S == D.StateType> { 

    var currentState: S { 
     didSet { 
      // The observer 
      if let delegate = self.delegate { 
       delegate.stateMachine(self, didEnterState: self.currentState) 
      } 
     } 
    } 

    var delegate: D? 

    init(initialState: S) { 
     self.currentState = initialState 
    } 


} 


protocol StateMachineDelegate: class { 
    typealias StateType: Printable 

    // Workaround with autoclosure 
    func stateMachine(machine: @autoclosure() -> StateMachine<StateType, Self>, didEnterState newState: StateType) 
} 

final class ADelegate: StateMachineDelegate { 
    typealias StateType = Int 
    func stateMachine(machine: @autoclosure () -> StateMachine<StateType, ADelegate>, didEnterState newState: StateType) { 
     // Need to _unbox_ the sander from the closure 
     let sender = machine() 
     println(newState) 
     println("State from sender: \(sender.currentState)") 
    } 
} 

let stateMachine = StateMachine<Int, ADelegate>(initialState: 24) 

stateMachine.delegate = ADelegate() 
stateMachine.currentState = 50 

वैसे, विचार है कि आप Sander मिलता है, शायद आप डॉन newState पास करने की आवश्यकता नहीं है। उदाहरण के लिए Hashable के स्थान पर मैंने Printable का उपयोग किया।

संबंधित मुद्दे