Programming with Contracts
Contracts are different from class implementations. A contract merely defines the requirements of a particular class; it does not actually control the class implementation. To continue the combatant analogy: A Combatant base class defines behavior that all child classes can inherit. A Combatant contract defines the behavior and data that must be implemented by any class wanting to call itself a combatant.
Let's walk through an example while assuming we're still building a game engine. If we go with a straight inheritance hierarchy, we might be limiting the types of interactions we can model. Assuming single inheritance (which is the case for both Objective-C and C#), anything that can do damage to a player (via the Attack method) must inherit from Combatant. This presents us with a problem: What about complex inanimate objects with a unique inheritance hierarchy but that cannot inherit from Combatant?
Let's say the player is walking through an alley and is struck by a car. Vehicles in this game might require their own inheritance hierarchy, probably starting with a base class such as Vehicle or MovingObject. Given that we don't have the capability to do multiple inheritance, how do we allow noncombatant objects to do damage to players without messing up the Combatant object hierarchy? The answer is contracts.
Contracts are called protocols in Objective-C and interfaces in C#, but they serve identical purposes. Contracts define a minimum set of required properties or methods that must be implemented by a particular class. They do not enforce any restrictions on inheritance hierarchies. It is critically important here to remember that two classes, each with entirely different inheritance hierarchies, can implement the same interface.
So let's take a look at the Combatant class. The Attack method does two things (which might give us a clue that we can start refactoring there): It figures out how much damage to do to an opponent, and then it asks the other combatant to take that damage. If we take out the function of taking the actual damage and make that a requirement on an interface called ITakesDamage, we start getting some real flexibility in our game engine. This interface has a requirement that anything implementing that interface must implement a method called TakeDamage.
Listing 3.15 shows the ITakesDamage interface in C#, and Listing 3.16 shows the new Combatant class, refactored to separate out the concern of doing damage to be something that satisfies the interface requirement.
Listing 3.15. ITakesDamage.cs
namespace Chapter3 { public interface ITakesDamage { void TakeDamage(int hitPoints); } }
Listing 3.16 shows the refactored Combatant class to implement the interface.
Listing 3.16. Combatant.cs (Refactored to Implement IDoesDamage)
using System; using System.Net; using System.Windows; using System.Diagnostics; namespace Chapter3 { public class Combatant : ITakesDamage { public int MaxHitpoints { get; set; } public int CurrentHitPoints { get; private set; } public int ArmorClass { get; set; } public int DamageClass { get; set; } public string Name { get; set; } public Point Location { get; protected set; } public Combatant() { this.CurrentHitPoints = this.MaxHitpoints; } public int HitPointPercent { get { double pct = (double)CurrentHitPoints / (double)MaxHitpoints; return (int)Math.Floor(pct * 100); } } public virtual void MoveTo(Point newLocation) { this.Location = newLocation; Debug.WriteLine("Combatant {0} just moved to ({1},{2})", this.Name, this.Location.X, this.Location.Y); } public virtual void Attack(Combatant target) { Random r = new Random(); int damage = (this.DamageClass - target.ArmorClass) * r.Next(20); target.TakeDamage(damage); } public void TakeDamage(int hitPoints) { this.CurrentHitPoints -= hitPoints; } } }
The new Attack method on the Combatant class now determines the amount of damage to be done and then calls the TakeDamage method to affect the target. Now that the Combatant class isn't the only thing in the game engine that can be damaged (anything that implements ITakesDamage can now be harmed), we can create classes like the PincushionTarget (shown in Listing 3.17), which can be harmed by players but is not a combatant.
Listing 3.17. PincushionTarget.cs
public class PincushionTarget : ITakesDamage { void TakeDamage(int hitPoints) { // take points out of pincushion target } }
For reference, Listing 3.18 shows what the protocol definition might look like in Objective-C. Objective-C does not use the uppercase "I" prefix naming convention but rather uses the word "Protocol" as a postfix. To achieve a similar goal in Objective-C, we would create a protocol called TakesDamageProtocol like the one shown in Listing 3.19. I show you this because protocols are used extensively throughout iOS and in UIKit, so recognizing how those patterns translate into C# patterns can be very useful.
Listing 3.18. TakesDamageProtocol.h
@protocol TakesDamageProtocol - (void)takeDamage:(int)hitPoints; @end