2016-12-05 3 views
6

Ich beschloss, ein besseres Verständnis der Typ Löschung zu erhalten, indem Sie einigen einfachen Code zu schreiben. Ich habe ein generisches Soldier-Protokoll. Soldaten haben Waffen und Soldaten können kämpfen. Ich würde gerne eine Armee von verschiedenen Arten von Soldaten schaffen. Ich dachte, diese Art der Auslöschung würde mir die Möglichkeit geben, die Soldier Adopters so zu verpacken, dass ich sie als einfache Soldaten behandeln könnte (anstelle von Snipern, Infanteristen usw.). Aber ich fand, dass der intermediäre, boxende Typ (der Typ-Radierer) muss immer noch generisch über Soldier's assoziierten Typ (dh Waffe) gemacht werden. Also kann ich Gewehr mit Soldaten oder Raketen mit Soldaten, aber nicht nur einfache Soldaten haben. Gibt es etwas über die Verwendung von Typ löschen, die ich verpasst habe?Type Erasure: Habe ich etwas verpasst?

import Foundation 

// Soldiers have weapons and soldiers can fight 

protocol Weapon { 
    func fire() 
} 

protocol Soldier { 
    associatedtype W: Weapon 

    var weapon: W { get } 

    func fight() 
} 

extension Soldier { 
    func fight() { weapon.fire() } 
} 

// Here are some weapons 

struct Rifle : Weapon { 
    func fire() { print("Bullets away!") } 
} 

struct Rocket : Weapon { 
    func fire() { print("Rockets away!") } 
} 

struct GrenadeLauncher : Weapon { 
    func fire() { print("Grernades away!") } 
} 

// Here are some soldiers 

struct Sniper : Soldier { 
    var weapon = Rifle() 
} 

struct Infantryman : Soldier { 
    var weapon = Rifle() 
} 

struct Artillaryman : Soldier { 
    var weapon = Rocket() 
} 

struct Grenadier : Soldier { 
    var weapon = GrenadeLauncher() 
} 

// Now I would like to have an army of soldiers but the compiler will not let me. 
// error: protocol 'Soldier' can only be used as a generic constraint because it has Self or associated type requirements 

class Army { 
    var soldiers = [Soldier]() 

    func join(soldier: Soldier) { 
     soldiers.append(soldier) 
    } 

    func makeWar() { 
     for soldier in soldiers { soldier.fight() } 
    } 
} 

// So, let's try the type erasure technique: 

struct AnySoldier<W: Weapon> : Soldier { 
    var weapon: W 
    private let _fight:() -> Void 

    init<S: Soldier>(soldier: S) where S.W == W { 
     _fight = soldier.fight 
     weapon = soldier.weapon 
    } 

    func fight() { _fight() } 
} 

var s1 = AnySoldier(soldier: Sniper()) 
print (type(of: s1)) // AnySoldier<Rifle> 
s1.fight() // Bullets away! 
s1.weapon.fire() // Bullets away! 
s1 = AnySoldier(soldier: Infantryman()) // Still good; Infantrymen use rifles 
s1 = AnySoldier(soldier: Grenadier()) // Kaboom! Grenadiers do not use rifles 


// So now I can have an army of Rifle wielding Soldiers 

class Army { 
    var soldiers = [AnySoldier<Rifle>]() 

    func join(soldier: AnySoldier<Rifle>) { 
     soldiers.append(soldier) 
    } 

    func makeWar() { 
     for soldier in soldiers { soldier.fight() } 
    } 
} 
let army = Army() 
army.join(soldier: AnySoldier(soldier: Sniper())) 
army.join(soldier: AnySoldier(soldier: Infantryman())) 
army.join(soldier: AnySoldier(soldier: Grenadier())) // Kaboom! Rifles only 
army.makeWar() 

// But, what I really want is an army wherein the weapons are unrestricted. 
class Army { 
    var soldiers = [AnySoldier]() 

    func join(soldier: AnySoldier) { 
     soldiers.append(soldier) 
    } 

    func makeWar() { 
     for soldier in soldiers { soldier.fight() } 
    } 
} 

Antwort

5

Sie müssen Waffen geben abwischbaren auch:

struct AnyWeapon: Weapon { 
    private let _fire:() -> Void 
    func fire() { _fire() } 
    init<W: Weapon>(_ weapon: W) { 
     _fire = weapon.fire 
    } 
} 

Damit AnySoldier nicht generisch sein muss.

Übersehen Sie nicht einen anderen Ansatz, der Waffe mit einer einfachen Funktion ersetzen und Soldier zu einer einfachen Struktur machen soll. Zum Beispiel:

struct Soldier { 
    private let weaponFire:() -> Void 
    func fight() { weaponFire() } 
    static let sniper = Soldier(weaponFire: { print("Bullets away!") }) 
} 

let sniper = Soldier.sniper 
sniper.fight() 

ich einige dieser weiter in Beyond Crusty: Real-World Protocols diskutieren. Manchmal wollen Sie kein Protokoll.

+0

Das war es, Rob. Vielen Dank. Übrigens: Ich liebe deine Gespräche. Ich werde Beyond Crusty wiedersehen - sobald ich mir Zeit nehme, das Original zu sehen. – Verticon

Verwandte Themen