ttrs ttrs - 4 months ago 20
Swift Question

Swift Prototype Design Pattern

Can someone help me to understand this Swift code? This code is an implementation of prototype design pattern according to book I'm currently reading:

class AbstractCard {
var name: String?
var mana: Int?
var attack: Int?
var defense: Int?
init(name:String?, mana:Int?, attack:Int?, defense:Int?) {
self.name = name
self.attack = attack
self.defense = defense
self.mana = mana
}
func clone() -> AbstractCard {
return AbstractCard(name: self.name, mana: self.mana, attack:
self.attack, defense: self.defense)
}
}
class Card: AbstractCard {
var someNumber: Int
override init(name:String?, mana:Int?, attack:Int?, defense:Int? ){
someNumber = 2
super.init(name: name,mana: mana,attack: attack,defense:
defense)
}
}
// Simulate our client
// This is the card that we will copy
let raidLeader = Card(name: "Raid Leader", mana: 3, attack: 2, defense: 2)
// Now we use our faceless Manipulator card to clone the

let facelessManipulator = raidLeader.clone()
print("\(facelessManipulator.name, facelessManipulator.mana, facelessManipulator.attack, facelessManipulator.defense)")
print(facelessManipulator.dynamicType) // prints "AbstractCard"


What's the point of this pattern if dynamicType of cloned object is still AbstractCard and not Card. You can't even access vars specific to Card. I tried to cast this object as Card but I get an error
"Execution was interrupted reason: signal SIGABRT"

Answer

Let's be clear about the Prototype pattern first:

The prototype pattern creates new objects by copying an existing object, known as the prototype.

First of all there are many ways of implement the Prototype pattern, I think the author of the book in this case try to explain you in the most appropriate way a user case of the pattern.

So as somebody put in the comments the clone method should be override in the subclass to allow the clone of the object properly, like in the following way:

class Card: AbstractCard {

   var someNumber: Int

   override init(name:String?, mana:Int?, attack:Int?, defense:Int? ){
      someNumber = 2
      super.init(name: name,mana: mana,attack: attack,defense:
        defense)
   }

   override func clone() -> AbstractCard {
      return Card(name: self.name, mana: self.mana, attack:
        self.attack, defense: self.defense)
   }
}

I have to said that I have worked as a Technical Reviewer for this book and I think the writer express in the better way he can the use of this pattern.

If you think carefully this pattern is applied every time you use value types in Swift (Arrays, Int, Bool, etc), because every time you assign the value of an instance to another one a new copy is made with the same values. The problem is with reference values (classes) that every time you assign an instance to a new one both share its values and modifications to they.

Another way of implement the pattern is using the NSCopying protocol that declares a method for providing functional copies of an object, and in my opinion the most used.

For example:

class Card: NSObject, NSCopying {

   var name: String?
   var mana: Int?
   var attack: Int?
   var defense: Int?

   init(name:String?, mana:Int?, attack:Int?, defense:Int?) {
      self.name = name
      self.attack = attack
      self.defense = defense
      self.mana = mana
   }

   func copyWithZone(zone: NSZone) -> AnyObject {
       return Card(name: self.name, mana: self.mana, attack: self.attack, defense: self.defense)
   }
}

// Simulate our client
// This is the card that we will copy
let raidLeader = Card(name: "Raid Leader", mana: 3, attack: 2, defense: 2)
// Now we use our faceless Manipulator card to clone the

let facelessManipulator = raidLeader.copy() as Card
raidLeader.attack = 5

print("\(facelessManipulator.name, facelessManipulator.mana,   facelessManipulator.attack, facelessManipulator.defense)")
print("\(raidLeader.name, raidLeader.mana, raidLeader.attack, raidLeader.defense)")

You can learn more about this way in this question:

I hope this help you.