01 Jul 2015
We all have our special weapons. And we fight for what we believed in.
enum PersonType{ case God case Knight case Wizard case Dwarf case Poet } protocol Personable { var personType: PersonType { get } var name: String { get } var weapon: Weapon? { get set} } class Person: Personable, CustomStringConvertible { var personType: PersonType var name: String var weapon: Weapon? var description: String { return name + (weapon == nil ? " - no weapon \n" : " - Weapon: \(weapon!.name) (price: \(weapon!.price)#) \n") } init(personType: PersonType,name: String, weapon: Weapon?){ self.name = name self.personType = personType self.weapon = weapon } } //: HouseOfWeapon is a new family that has rised since the first war in the seven kindom. They are the only place where people can get their weapons. protocol Weapon { var name: String { get } var price: Int { get set } func fight() } struct Sword: Weapon { var name: String var price: Int func fight() { print("fight with sword") } } struct Pen: Weapon { var name: String var price: Int func fight() { print("fight with pen") } } struct Magic: Weapon { var name: String var price: Int func fight() { print("fight with magic") } } struct Hammer: Weapon { var name: String var price: Int func fight() { print("fight with hammer") } } //: In the beginning, people would randomly drop at the HouseOfWeapon and ask for their weapon of choice. The choices sometimes turn out to be terribly wrong and people would come back to demand for exchanges. There is no system //: LordOfWeapon is the head of HouseOfWeapon and he has the knowledge of what kind of weapon should be create for what kind of person. class LordOfWeapon { class func weaponMatch(person: Person) -> Weapon { switch person.personType { case .Dwarf: return Hammer(name: "Hammer", price: 10) case .God, .Wizard: return Magic(name: "Magic", price: 20) case .Knight: return Sword(name: "Sword", price: 15) case .Poet: return Pen(name: "Pen", price: 25) } } } //: create a group of people let knight = Person(personType: PersonType.Knight, name: "Knight William", weapon: nil) let dwarf = Person(personType: PersonType.Dwarf, name: "Dwarf Jim", weapon: nil) let poet = Person(personType: PersonType.Poet, name: "Poet Shelly", weapon: nil) let god = Person(personType: PersonType.God, name: "God of Music", weapon: nil) var group:[Person] = [knight, dwarf, poet, god] print("----------People Group Created---------") print(group.map{$0.description}) group = group.map({ (var p) -> Person in p.weapon = LordOfWeapon.weaponMatch(p) return p }) print("----------Weapon Added---------") print(group.map{$0.description}) //:## Now, the new LordOfCoin want to reset the price of the weapons class LordOfCoin { class func adjustWeaponPrice( person: Person) -> Int { guard person.weapon != nil else { return 0 } switch person.personType { case .Dwarf: return 1 case .God,; .Wizard: return 50 case; .Knight: return 5 case; .Poet: return -20 } } } print("\n----------LordOfCoin adjusted the price of weapons ---------") group = group.map({ (var p) -> Person in p.weapon?.price += LordOfCoin.adjustWeaponPrice(p) return p }) print(group.map{$0.description}) //: The problem is that both LordOfCoin and LordOfWeapon are doing the same kind of checking for person's type. Can they outsource this work to someone else? Perhaps a RuleBook? Say a universalWeaponMatch rule func universalWeaponMatch(var person: Personable) -> Personable { if person.weapon == nil { var weapon: Weapon switch person.personType { case .Dwarf: weapon = Hammer(name: "Hammer", price: 10) case .God, .Wizard: weapon = Magic(name: "Magic", price: 20) case .Knight: weapon = Sword(name: "Sword", price: 15) case .Poet: weapon = Pen(name: "Pen", price: 25) } person.weapon = weapon } return person } //: The LordOfWeapon refers the 'universalWeaponMatch' to create the right weapon for each person class LordOfWeaponAdvanced { class func weaponMatch(person: Personable) -> Weapon? { return universalWeaponMatch(person).weapon } } //: CoinOfWeapon refers the 'universalWeaponMatch' to check and adjust the weapon price. class LordOfCoinAdvanced { class func getWeaponPrice(person: Personable) -> Int? { return universalWeaponMatch(person).weapon?.price } class func adjustWeaponPrice(var person: Personable) -> Personable { //: equip the person with weapon person = universalWeaponMatch(person) guard person.weapon != nil else { return person } // Fav Poet and give them free weapons if person.weapon!.price > 0 && person.personType == .Poet { print("Through the 'universalWeaponMatch' book, LordOfCoinAdvanced adjusted the weapon price for \(person.name) from \(person.weapon!.price)# to 0# ") person.weapon!.price = 0 } return person } } print("\n---------- 'universalWeaponMatch' book is created ---------") LordOfCoinAdvanced.adjustWeaponPrice(group[2]) print(group[2] )
Rework on the code, add weapon factories and let them decide the weapon details.
enum PersonType{ case God case Knight case Wizard case Dwarf case Poet } enum WeaponType { case Sword case Magic case Pen case Hammer } //: We all have our special weapons. And we fight for what we believed in. protocol Personable { var personType: PersonType { get } var name: String { get } var weapon: Weapon? { get set} var skillLevel: Int { get set} } class Person: Personable, CustomStringConvertible { var personType: PersonType var name: String var weapon: Weapon? var skillLevel: Int var description: String { return name + (weapon == nil ? " - no weapon \n" : " - Weapon: \(weapon!.name) (price: \(weapon!.price)#) \n") } init(personType: PersonType,name: String, weapon: Weapon?, skillLevel: Int = 0){ self.name = name self.personType = personType self.weapon = weapon self.skillLevel = skillLevel } } protocol Weapon { var name: String { get } var price: Int { get set } var powerLevel: Int { get } func fight() } struct Sword: Weapon { var name: String var price: Int var powerLevel: Int func fight() { print("fight with sword") } } struct Pen: Weapon { var name: String var price: Int var powerLevel: Int func fight() { print("fight with pen") } } struct Magic: Weapon { var name: String var price: Int var powerLevel: Int func fight() { print("fight with magic") } } struct Hammer: Weapon { var name: String var price: Int var powerLevel: Int func fight() { print("fight with hammer") } } //: Previously the "universalWeaponMatch" takes over the responsibility for creating the matching weapon for each person. But as the amount of weapon grows, the matching book exploded and became hard to manage. //: In general, the HouseOfWeapon makes 3 kinds weapons: //: * WeaponFromTheSouth: good for entry level, flexbile //: * WeaponFromTheNorth: requires heavy mastery, very powerful //: * WeaponFromTheVast: a mix of materials from everywhere, can be very weak or very powerful. Hard to expect the result protocol WeaponFactory { func makeAWeapon(weaponType: WeaponType) -> Weapon } class WeaponFactoryFromTheNorth:WeaponFactory { func makeAWeapon(weaponType: WeaponType) -> Weapon { switch weaponType { case .Hammer: return Hammer(name: "Hammer from North", price: 100, powerLevel: 90) case .Magic: return Magic(name: "Magic from North", price: 100, powerLevel: 80) case .Pen: return Pen(name: "Pen from North", price: 90, powerLevel: 99) case .Sword: return Sword(name: "Sword from North", price: 70, powerLevel: 65) } } } class WeaponFactoryFromTheSouth:WeaponFactory { func makeAWeapon(weaponType: WeaponType) -> Weapon { switch weaponType { case .Hammer: return Hammer(name: "Hammer from South", price: 100, powerLevel: 1) case .Magic: return Magic(name: "Magic from South", price: 10, powerLevel: 18) case .Pen: return Pen(name: "Pen from South", price: 9, powerLevel: 9) case .Sword: return Sword(name: "Sword from South", price: 7, powerLevel: 5) } } } class WeaponFactoryFromTheVast:WeaponFactory { func makeAWeapon(weaponType: WeaponType) -> Weapon { switch weaponType { case .Hammer: return Hammer(name: "Hammer from Vast", price: 80, powerLevel: 99) case .Magic: return Magic(name: "Magic from Vast", price: 30, powerLevel: 1) case .Pen: return Pen(name: "Pen from Vast", price: 9, powerLevel: 67) case .Sword: return Sword(name: "Sword from Vast", price: 78, powerLevel: 2) } } } //: the 'universalWeaponMatchAdvanced' checks the person's skill level first and send the order to one of the three Weapon Factories to make the weapon. It doesn't need to care about the details of each weapon (price, name...). It's not their business anyway. func universalWeaponMatchAdvaned(var person: Personable) -> Personable { var weaponFactory: WeaponFactory switch person.skillLevel { case 0...20: weaponFactory = WeaponFactoryFromTheSouth() case; 21...100: weaponFactory = WeaponFactoryFromTheNorth(); default: weaponFactory = WeaponFactoryFromTheVast() } if person.weapon == nil { var weapon: Weapon switch person.personType { case .Dwarf: weapon = weaponFactory.makeAWeapon(WeaponType.Hammer) case .God,; .Wizard: weapon = weaponFactory.makeAWeapon(WeaponType.Magic) case; .Knight: weapon = weaponFactory.makeAWeapon(WeaponType.Sword) case; .Poet: weapon = weaponFactory.makeAWeapon(WeaponType.Pen) } person.weapon = weapon } return person } class LordOfWeapon { class func weaponMatch(person: Personable) -> Weapon? { return universalWeaponMatchAdvaned(person).weapon } } //: Testing print("----------Created a group of people ---------") let knight = Person(personType: PersonType.Knight, name: "Knight William", weapon: nil, skillLevel: 56) let dwarf = Person(personType: PersonType.Dwarf, name: "Dwarf Jim", weapon: nil, skillLevel: 99) let poet = Person(personType: PersonType.Poet, name: "Poet Shelly", weapon: nil, skillLevel: 142) let god = Person(personType: PersonType.God, name: "God of Music", weapon: nil, skillLevel: 1) var group:[Person] = [knight, dwarf, poet, god] print(group.map{$0.description}) print("----------Add Weapon to each person according to their skill level ---------") group.map{LordOfWeapon.weaponMatch($0)} print(group.map{$0.description}) //:## Now, the new LordOfCoin want to reset the price of the weapons. He is also responsible for calculating the tax for each person's weapon class LordOfCoin { class func calculateWeaponCostAfterTax(person: Personable) -> Int { if person.weapon == nil { return 0 } else { return universalWeaponMatchAdvaned(person).weapon!.price * 2 } } class func adjustWeaponPrice( person: Person) -> Int { guard person.weapon != nil else { return 0 } switch person.personType { case .Dwarf: return 1 case .God, .Wizard: return 50 case .Knight: return 5 case .Poet: return 20 } } } print("\n----------LordOfCoin adjusted the price of weapons ---------") group = group.map({ (var p) -> Person in p.weapon?.price += LordOfCoin.adjustWeaponPrice(p) return p }) print(group.map{$0.description}) print("\n----------LordOfCoin calculate the total cost after tax ---------") let totalCost = group.map{LordOfCoin.calculateWeaponCostAfterTax($0)}.reduce(0, combine: +) let totalCostBeforeTax = group.map{$0.weapon!.price}.reduce(0, combine: +) print("total cost after tax: \(totalCost)# (before tax: \(totalCostBeforeTax)#)\n")