Encapsulating Data
One of the most important things that any class can do is to encapsulate data. This is one of the main reasons for the original use of object-oriented programming.
Data encapsulation involves a few key concepts that each programming language implements differently:
- Store member variables
- Provide wrappers around accessing those variables
- Add scope and security (for example, read-only) to member variables
Objective-C in its earlier days had somewhat limited support for what most modern programming languages call properties, but now has full and robust capabilities for data encapsulation that rival those of C#.
The best way to see data encapsulation in action is to look at some code with member variables in it. In these next few code listings, we're going to add member variables to the Combatant class that will model some of the properties of a combatant that we know the game engine might need, such as hit points, armor class, damage class, and a few other details.
Listing 3.4 shows how we might declare properties in the header file of an iOS application.
Listing 3.4. Combatant.h with Member Variables
@interface Combatant : NSObject { } @property(nonatomic, assign) int maxHitPoints; @property(nonatomic, assign) int currentHitPoints; @property(nonatomic, assign) int armorClass; @property(nonatomic, assign) int damageClass; @property(nonatomic, retain) NSString *combatantName;
This should be fairly familiar to most iOS developers. We have a couple of int-based properties to store values such as hit points and armor class, and there is a string property for storing the combatant name. Using the @property syntax, we can specify that the autogenerated accessor for the combatantName property will automatically retain the string. In C#, we don't need to worry about retaining strings as a precaution against unintended disposal like we do in Objective-C. Listing 3.5 shows the implementation that automatically synthesizes the getter and setter accessors for the properties declared in Listing 3.4.
Listing 3.5. Combatant.m with Member Variables
#import "Combatant.h" @implementation Combatant @synthesize maxHitPoints, currentHitPoints, armorClass, damageClass, combatantName; @end
In the implementation (.m) file, I'm using the @synthesize keyword to instruct Xcode that it should autogenerate accessors for those properties. How Xcode does so is governed by the information in the header file in the @property declaration. If I wanted to, I could manually override this autogeneration process and supply my own accessor for specific properties, or even replace a get or a set accessor.
Listing 3.6 shows the C# version of the Combatant class, including the automatic implementation of the property get and set accessors.
Listing 3.6. Combatant.cs with Member Variables
using System; using System.Net; using System.Windows; namespace Chapter3 { public class Combatant { public int MaxHitpoints { get; set; } public int CurrentHitPoints { get; set; } public int ArmorClass { get; set; } public int DamageClass { get; set; } public string Name { get; set; }| public Point Location { get; set; } public int HitPointPercent { get { double pct = (double)CurrentHitPoints / (double)MaxHitpoints; return (int)Math.Floor(pct * 100); } } } }
Listing 3.6 shows the basic Combatant class with several public properties that use a shortcut syntax available in C#. This shortcut syntax allows the developer to leave the get and set implementations blank. When these accessors are left blank, the compiler will automatically generate a private member variable to back the public property and do all the required plumbing on behalf of the developer. This type of "automatic property" is also available in iOS.
The public keyword in front of each of the property names indicates that the property is visible and accessible from any class in any assembly. If the keyword were changed to internal, the properties would be available only to other classes within that assembly. Finally, the private keyword indicates that only that class has access to those members. You will learn about the protected keyword later in the chapter when we get into inheritance and object hierarchies.
The last property, HitPointPercent, shows an example of how you can create a read-only property that computes its value dynamically based on other properties. This shows another example of data encapsulation in that it allows the developer to hide the complexity of a calculation behind a simple property. In this example it's a simple percentage calculation, but you can imagine how this kind of technique can come in handy when modeling complex business objects with very detailed rules and logic. Also note that we have to typecast each of the integer values in the division calculation to a floating point value; otherwise, the / operator would assume integer division and return 0 instead of a fractional value.