Intro to Object-Oriented Design: Part 1/2

This tutorial series will teach you the basics of object-oriented design. In this first part: Inheritance, and the Model-View-Controller pattern. By Ellen Shapiro.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 5 of this article. Click here to view the first page.

A Minor Digression: Class Methods vs. Instance Methods

You’ve probably noticed when writing code that some methods have a + and some methods have a in front of them. These indicate whether a method is a Class method or an Instance method.

The simplest way to think of the difference is like a schematic blueprint in the physical world: There’s only one blueprint for something, but using that blueprint, you can make many different copies.

A class method, represented by the + symbol, is an action that can be performed with that blueprint, but without creating a specific copy of the object from that blueprint. For example, NSString has the stringWithFormat: class method to create new string objects.

An instance method, represented by the symbol, requires a specific copy of the object created using that blueprint to perform any action. For example, the NSString instance @"Hello There" has an instance method lowercaseString that would return @"hello there". A class method lowercaseString wouldn’t make any sense since that’s just the blueprint for a string – there’s no actual text to lower case!

Adding Basic Methods to Your Class

Go to Vehicle.h and add the following method declarations to the header file, just below the properties you added earlier, and before @end:

//Basic operation methods
-(NSString *)goForward;
-(NSString *)goBackward;
-(NSString *)stopMoving;
-(NSString *)changeGears:(NSString *)newGearName;
-(NSString *)turn:(NSInteger)degrees;
-(NSString *)makeNoise;

A method declaration in a header file is public – it tells other objects looking at your class, “Here’s what I’m going to be able to do”, but it doesn’t give any details of how it’s done. To do that, open Vehicle.m and add the following implementations, or actual code to execute, for each of these methods:

-(NSString *)goForward
{
    return nil;
}

-(NSString *)goBackward
{
    return nil;
}

-(NSString *)stopMoving
{
    return nil;
}

-(NSString *)turn:(NSInteger)degrees
{
    //Since there are only 360 degrees in a circle, calculate what a single turn would be.
    NSInteger degreesInACircle = 360;
    
    if (degrees > degreesInACircle || degrees < -degreesInACircle) {
        //The % operator returns the remainder after dividing. 
        degrees = degrees % degreesInACircle;
    }
    
    return [NSString stringWithFormat:@"Turn %d degrees.", degrees];
}

-(NSString *)changeGears:(NSString *)newGearName
{
    return [NSString stringWithFormat:@"Put %@ into %@ gear.", self.modelName, newGearName];
}

-(NSString *)makeNoise
{
    return nil;
}

Most of this code is just the skeleton of setting up the methods; you'll fill in the implementation details later. The turn: and changeGears: methods have some logging output too, which will help you test that your methods are working before you continue any further in the tutorial.

Open AppDelegate.m, and add an import to the top of the file:

#import "Vehicle.h"

This will allow you to access the Vehicle class from your own code.

Next, replace the implementation of application:didFinishLaunchingWithOptions: with the following:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    Vehicle *vehicle = [[Vehicle alloc] init];
    //Test methods with implementations
    NSLog(@"Vehicle turn: %@", [vehicle turn:700]);
    NSLog(@"Vehicle change gears: %@", [vehicle changeGears:@"Test"]);
    
    //Test methods without implementations
    NSLog(@"Vehicle make noise: %@", [vehicle makeNoise]);
    NSLog(@"Vehicle go forward: %@", [vehicle goForward]);
    NSLog(@"Vehicle go backward: %@", [vehicle goBackward]);
    NSLog(@"Vehicle stop moving: %@", [vehicle stopMoving]);
    
    return YES;
}

Once the vehicle instance is instantiated, you'll call each of the instance methods and log the output to see what they do.

Build and run your app; you’ll see that all implemented strings return proper logs with the appropriate items filled in. But anywhere a property isn’t set or a method returns nil, you’ll see a (null), as follows:

Log output

You'll be using Inheritance to provide more specific implementations of each of these methods.

Inheritance

The basic concept of inheritance is similar to that of genetics: children inherit the characteristics of their parents.

However, it’s a lot more strict than real world genetics in a single-inheritance language like Objective-C. Rather than having two parents from whom you inherit a mix of characteristics, “child” classes, or subclasses, inherit all the characteristics of their “parent” classes, or superclasses.

NSObject, which Vehicle inherits from, is the lowest-level class you can use in Objective-C. It’s the parent class to almost all of the everyday Objective-C classes.

Note: There are some C structs like CGRect and CGSize that don't use NSObject as their parent class, since structs don’t really adhere to Object-Oriented programming. However, the vast majority of classes with the prefix NS or UI have NSObject as their parent class. Check out Apple's documentation to learn more about NSObject.

To see inheritance in action, create a subclass of Vehicle, “Car”. Go to File\New\File...\Cocoa Touch\Objective-C Class. Then, create a subclass of Vehicle called Car, as shown below:

Add Car

Now open Car.m add the following initialization method under the @implementation line:

- (id)init
{
  if (self = [super init]) {
    // Since all cars have four wheels, we can safely set this for every initialized instance
    // of a car. 
      self.numberOfWheels = 4;
  }
  return self;
}

This init implementation simply sets the number of wheels to 4.

Did you notice that you didn’t have to do anything extra to access the numberOfWheels property of the Vehicle parent class? That’s because by inheriting from the Vehicle class, Car already knows about all of Vehicle’s public properties and methods.

But what if you need more information to describe the car? Cars have more specific characteristics than just the number of wheels How many doors does it have? Is it a hatchback or does it have a normal trunk? Is it a convertible? Does it have a sunroof?

Well, you can easily add those new properties! Open Car.h and add the following new properties under the @interface line:

@property (nonatomic, assign) BOOL isConvertible;
@property (nonatomic, assign) BOOL isHatchback;
@property (nonatomic, assign) BOOL hasSunroof;
@property (nonatomic, assign) NSInteger numberOfDoors;

Overriding Methods

Now that you’ve added the appropriate additional properties, you can add one new method and override several methods from the superclass to provide full implementations of those methods.

Overriding a method means “taking a method declared in the superclass and creating your own implementation.” 

For example, when you add a new UIViewController object, it already comes with overridden methods for initWithNibName:bundle:, viewDidLoad, and didReceiveMemoryWarning.

When you override a method, you can do one of two things:

  1. Include a call to the [super method] to take advantage of everything happening higher up the inheritance chain, or
  2. Provide your own implementation from scratch.

In all of the UIViewController methods, you can tell that Apple wants you to call the [super method] - there’s some important stuff in there that needs to execute before your UIViewController subclass can do its work.

However, since most of the methods you're going to override in the Car class are returning nil, you can just create your own implementations. There's nothing useful in the superclass's implementation so there's no need to call it.

Open Car.m and add the following private method to simplify your superclass override:

#pragma mark - Private method implementations
- (NSString *)start
{
    return [NSString stringWithFormat:@"Start power source %@.", self.powerSource];
}

Some vehicles such as bicycles don't need to be started, but cars do! In this case, you're not publicly declaring start since it should only be called within the implementation.

Note: Even though a method is "private" and other objects and classes can't see it, that doesn't protect a subclass from overriding the method. You really can't stop something from going wrong in this case, but you should make notes in your app's documentation, just as Apple does.

Next, add the remaining superclass overrides:

#pragma mark - Superclass Overrides
- (NSString *)goForward
{
    return [NSString stringWithFormat:@"%@ %@ Then depress gas pedal.", [self start], [self changeGears:@"Forward"]];
}

- (NSString *)goBackward
{
    return [NSString stringWithFormat:@"%@ %@ Check your rear view mirror. Then depress gas pedal.", [self start], [self changeGears:@"Reverse"]];
}

- (NSString *)stopMoving
{
    return [NSString stringWithFormat:@"Depress brake pedal. %@", [self changeGears:@"Park"]];
}

- (NSString *)makeNoise
{
    return @"Beep beep!";
}

Now that you have a concrete, or fully implemented, subclass of Vehicle, you can start building out your Table View controller.

Contributors

Over 300 content creators. Join our team.