我决定通过编写一些简单的代码来更好地理解类型擦除.我有一个通用的士兵协议.士兵有武器,士兵可以战斗.我想创建不同类型士兵的军队.我认为类型擦除会为我提供一种拳击士兵采用者的方法,以便我可以将它们视为普通士兵(而不是狙击手,步兵等)但我发现中间,拳击类型(类型橡皮擦)必须在士兵的相关类型(即武器)上仍然是通用的.所以,我可以让步枪挥舞士兵,或者火箭挥舞士兵而不仅仅是普通的士兵.有没有关于我错过的类型擦除的使用?
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() }
}
}
Run Code Online (Sandbox Code Playgroud)
您还需要键入 - 擦除武器:
struct AnyWeapon: Weapon {
private let _fire: () -> Void
func fire() { _fire() }
init<W: Weapon>(_ weapon: W) {
_fire = weapon.fire
}
}
Run Code Online (Sandbox Code Playgroud)
有了这个,AnySoldier不需要是通用的.
struct AnySoldier : Soldier {
private let _fight: () -> Void
let weapon: AnyWeapon
init<S: Soldier>(_ soldier: S) {
_fight = soldier.fight
weapon = AnyWeapon(soldier.weapon)
}
func fight() { _fight() }
}
Run Code Online (Sandbox Code Playgroud)
但是,不要忽视另一种方法,即用简单的函数替换武器,使Soldier成为一个简单的结构.例如:
struct Soldier {
private let weaponFire: () -> Void
func fight() { weaponFire() }
static let sniper = Soldier(weaponFire: { print("Bullets away!") })
}
let sniper = Soldier.sniper
sniper.fight()
Run Code Online (Sandbox Code Playgroud)
我将在Beyond Crusty:Real-World Protocols中进一步讨论其中的一些内容.有时您不需要协议.
| 归档时间: |
|
| 查看次数: |
518 次 |
| 最近记录: |