How to Properly Encapsulate Your Classes in Objective-C   1 comment


Difficulty: easy moderate challenging

The main task of a programmer is not simply to create code that works, but perhaps even more important, to create code that’s manageable, adaptable to new requirements, and allows easy debugging. These qualifications are intended to help programmers, either the original coder or his or her colleagues, to better support the code; the CPU doesn’t need any help – it knows precisely how to handle all instructions, be them cluttered or neat. So, to be able to support those requirements, we strive to divide big chunks of code into separate files, functions, and lines.

In this regard, one of the major aspects of object-oriented programming is to properly encapsulate your code in distinct segment, namely Classes and their instances, Objects. The heart of object-oriented is to decouple one Class from others and even its instances from one another. At first, it sounds like decoupling limits a programmer’s abilities. Although this is true per se, decoupling helps us sustain our code-base as it grows in size.

Using a Black-Box approach helps us attain decoupling. In this approach, each class is a black box – it works as it is and doesn’t involve other classes in its process more than it has to. To put it in another perspective, classes don’t need to go into the details of other classes, especially not their implementation details.

In Objective-C, we use .h as header files and .m as implementation files. The purpose of having .h files is to allow classes to interact with each other. As per our previous discussion, a .h file should include the minimal required code to facilitate the interaction between this class and the others; the rest should go into the .m file.

So what do we must have in our .h file? First, our .h file should have an @interface directive, and its enclosing @end to indicate this new class. Beside this, we would want to include all methods that need to be called by other classes. For example:

#import “Key.h”
@interface Door
-       (BOOL) open;
-       (BOOL) lockUsingKey:(Key*)theKey;
-        (BOOL) unlockUsingKey:(Key*)theKey;
-        (void) close;
@end

To use a Key object as shown above, our “Door.h” has to know of it; so, it includes Key’s header file (line #1). Doing so, our Door class is now familiar with Key’s exported methods as well. So, the four methods’ implementation, coded in Door.m, can call back methods they exported in their respective .h files on the received objects. In the example above, Key is an object passed to our Door class’ instance when lockUsingKey gets called, for which we’ll be able to call [theKey rotate];, assuming that the rotate method is declared of Key’s header file. Keep in mind that properties are actually exported as a getter method, and for read-write ones, also as a setter method. If you are not entirely familiar with, properties, refer to Apple’s documentation: Objective-C manual, properties section.

An imperative part of our header file is still missing: documentation, as much as possible. Documentation is the only information, beside actual code, which users of this class see; therefore our documentation should thoroughly explain how to use our class, whether or not to subclass it, in what order should the methods be called, what methods must be called, what are the expected parameters, and everything you can probably think of. Always bring to your mind Apple’s excellent documentation as a reference.

As said, your header file should import related header files it uses for its own declaration. If, however, you use some other classes in your implementation file only, import those in the .m file instead.

There are a few other things you can have in your .h file, such as enums, typedefs, structs, other interfaces, protocols, and categories, to name a few. Just remember to ponder about each one whether its part of the contract for others to see or part of the implementation details; based on your answer, you need to put those in the .h or .m file, respectively.

One major drawback of Objective-C in fully supporting encapsulation is when adding member variables. Member variables’ declaration must be done in your .h files. As a rule, even though other classes can directly access your instances’ member variables, they should not, ever. Member variables are meant to hold raw data for their enclosing class; modifications in their data directly from the outside world would means your class can’t validate, control, or even react to those changes; such action can probably break its integrity rather quickly. So, even though variables are to be used directly by your class only, remember that they must be specified in your header file, even though they don’t really belong there.

This all sounds a bit more complicated and menacing than it actually is. As long as you’ll keep making your header file as short as possible, code-wise, while writing as much documentation as possible, you will inherently follow these rules.

One response to “How to Properly Encapsulate Your Classes in Objective-C

Subscribe to comments with RSS.

  1. Thank you for the great explanation

Leave a comment