Hide behind your interface
When writing a class or method, the way that I view other classes that want to use my class is as my enemies (even if I’m writing the other classes!). That might should dramatic but I take the approach that what ever is going to use my class might not properly understand the class and feed it junk.
Proper encapsulation and information hiding can help us achieve this. Often encapsulation and information hiding are used interchangeably however they are two independent ideas.
Encapsulation
Is the mechanism that we use to ensure that an object can only be affected through its API. This helps us to lessen the impact of changing one part of the system.
Information hiding
Humans can only handle a certain level of detail until we stop seeing the forest for the trees. The idea behind information hiding is to keep us seeing the forest by hiding away all the low-level details and allowing us to work with higher abstractions.
Now we know what we are trying to achieve lets look at ways that we can achieve it. If the outside doesn’t need to know about a method or have the ability to change an instance variable to do it’s job then we won’t provide that functionality.
Methods
Take the following example:
- (void)insertName:(NSString *)name atIndex:(NSUInteger)index
{
[self.names insertObject:name atIndex:index];
}
We can see that certain assumptions have been made:
- The parameter name will not be nil
- The parameter index will be within the range of our array
This method should be changed to:
- (BOOL)insertName:(NSString *)name atIndex:(NSUInteger)index
{
if (!name) {
return NO;
}
if (index > ([self.names count]-1)) {
return NO;
}
[self.names insertObject:name atIndex:index];
return YES;
}
In the new method we are really scrutinising the data that come in and ensuring that it meets the quality standards that we expect, we could go further and add a new parameter for an error object to be set but I don't think we need it in this example.
Properties
With properties we can prevent an outside class from setting the value of any instance variable through a property we can use the readonly property attribute.
@property (nonatomic, readonly) NSMutableArray *names;
If we want to allow the instance variable to be changed but only for certain valid values we could continue to use the readonly property attribute and create a method that returns a Boolean indicating if it has been successfully set.
- (BOOL)setNames:(NSMutableArray *)names
If we want to provide more information as to why the value was reject then we could use an NSError to do so.
- (BOOL)setNames:(NSMutableArray *)names error:(NSError **)error
The trouble with setting the property to readonly is we prevent our own class from being able to set the instance variable that property is on associated with of and instead we have to directly set the instance variable. If your project is pre-ARC can lead to some messy memory management code and introduce the opportunity for further bugs which is the exact opposite of what we are attempting to accomplish here. Thankfully Apple’s latest compiler, Apple LLVM complier 3.0, comes to the rescue here. Using a class extension we can override the readonly property attribute for our class so that we can access and set the instance variable freely through the property.
@interface STHidingExample()
@property (nonatomic, strong) NSMutableArray *names;
@end
@implementation STHidingExample
//rest of implementation
@end
A common bug, especially with inexperience developers, is to attempt to directly access an instance variable rather than use its properties because the same name is given to both the property and the instance variable. To prevent this type of bug from entering your code, it is better to assign it a different name
@synthesize names = _names;
By hiding behind our class interfaces we a striving to make the boundaries of our object clearly visible.