Intro To Object-Oriented Design: Part 2/2

This tutorial series will teach you the basics of object-oriented design. In this final part: polymorphism, factory methods, and singletons. By Ellen Shapiro.

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

Implementing the Adapter Pattern

Open VehicleDetailViewController.h and declare that it conforms to the UIAlertViewDelegate protocol by adding that protocol’s name in angle brackets, as so:

@interface VehicleDetailViewController : UIViewController <UIAlertViewDelegate>

If you were to translate the above line into English, it would read: “This is a VehicleDetailViewController, which is a subclass of UIViewController and conforms to the UIAlertViewDelegate protocol.” If a class conforms to multiple protocols, you can list them all in the angle brackets separated by commas.

Note: Implementing a specific protocol in a class is frequently called “conforming” to that protocol.

You’ll use all this to implement a mechanism that figures out how many degrees the user wants to turn their Vehicle.

Open VehicleDetailViewController.m and replace turn with the following implementation:

-(IBAction)turn
{
    //Create an alert view with a single text input to capture the number of degrees
    //to turn your vehicle. Set this class as the delegate so one of the delegate methods
    //can retrieve what the user entered.
    UIAlertView *turnEntryAlertView = [[UIAlertView alloc] initWithTitle:@"Turn" message:@"Enter number of degrees to turn:" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Go!", nil];
    turnEntryAlertView.alertViewStyle = UIAlertViewStylePlainTextInput;
    [[turnEntryAlertView textFieldAtIndex:0] setKeyboardType:UIKeyboardTypeNumberPad];
    [turnEntryAlertView show];
}

The method creates a UIAlertView with a text input that will prompt the user for a numeric value.

Next, you’ll need to add a delegate method for the UIAlertView instance to call back after the user enters a number. Add the following method:

#pragma mark - UIAlertViewDelegate method
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    //Note: Only one alert view will actually declare this class its delegate, so we can
    //      proceed without double-checking the alert view instance. If you have more than
    //      one alert view using the same class as its delegate, make sure you check which
    //      UIAlertView object is calling this delegate method.
    if (buttonIndex != alertView.cancelButtonIndex) {
        //Get the text the user input in the text field
        NSString *degrees = [[alertView textFieldAtIndex:0] text];
        
        //Convert it from a string to an integer
        NSInteger degreesInt = [degrees integerValue];
        
        //Use the simple alert view to display the information for turning. 
        [UIAlertView showSimpleAlertWithTitle:@"Turn" andMessage:[self.detailVehicle turn:degreesInt]];
    } //else the user has cancelled, and we don't need to do anything.
}

The above code implements a selected UIAlertViewDelegate method so you can detect when a button is clicked.

Build and run your project; select a Vehicle from the list, tap the Turn button and enter a number of degrees to turn, like so:

Turn 90 degrees

If you hit Cancel, nothing will happen since you’ve set your delegate to ignore that index. However, if you hit Go!, the first UIAlertView disappears and the following UIAlertView will appear:

Turn complete!

Your application is now functionally complete. However, what if you want to make your code a little more elegant so it’s easier to maintain and add to it later? It’s time to learn about two more object-oriented design patterns to make your coding life easier!

Additional Object-Oriented Patterns

While there are a ton of great patterns you can use with Object-Oriented programming (in fact, Eli Ganem has written an entire tutorial full of them), there are two that are quite useful in your Vehicles app’s context: the Class Factory Method and the Singleton Instance.

Both of these patterns are used extensively in iOS development, and understanding what they do under the hood will help you understand the design of the code you’ll encounter as an iOS developer.

Class Factory Methods

The concept of a Class Factory method is to return an instantiated object of a particular class with as much data pre-populated as possible. One of the obvious benefits of using a factory method rather than calling alloc/init and setting properties, is that your code can be much shorter.

In this way, you’re using one method to create an object and set all its properties rather than one method to create the object, then writing multiple lines to set all the properties. However there are also two less-obvious benefits to this technique.

One, it forces anyone using your class to provide exactly the information you need in order to create a fully functioning instance of your object. As you created the various objects in this tutorial, you may have noticed that it was easy to miss a property or two. With a factory method, you are forced to be conscious of exactly what information you are and are not providing about the object you want to create.

Two, and admittedly much less of a problem with ARC code, which is pretty much anything written since iOS 5, is that factory methods will return autoreleased objects, freeing the caller from having to release them later. You likely won’t need to worry about this issue unless you’re supporting old code, but it’s a good thing to be aware of.

Implementing the Vehicle Class Factory Method

Open Vehicle.h and declare the following class factory method which accepts parameters representing all of the basic properties of a vehicle:

//Factory Method
+ (instancetype)vehicleWithBrandName:(NSString *)brandName modelName:(NSString *)modelName modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource wheels:(NSInteger)numberOfWheels;

The instancetype type is a slightly safer version of id. Whereas an id parameter or return type can accept any subclass of NSObject, instancetype as a method signature tells you that you are definitely receiving an instance of the class or subclass of the class where the instance is being initialized.

Note: Read up on the details of instancetype over on NSHipster

One other thing to note with factory methods and inheritance: since they return a fully instantiated object, you have to be careful about how you use them in superclasses, as they return a particular class of object.

Go to Vehicle.m and add the following implementation of the factory method:

#pragma mark - Factory method
+ (instancetype)vehicleWithBrandName:(NSString *)brandName modelName:(NSString *)modelName modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource wheels:(NSInteger)numberOfWheels;
{
    //Use self in the superclass to ensure you're getting the proper return type for each of the subclasses. 
    Vehicle *newVehicle = [[self alloc] init];
    
    //Set the provided values to the appropriate instance variables.
    newVehicle.brandName = brandName;
    newVehicle.modelName = modelName;
    newVehicle.modelYear = modelYear;
    newVehicle.powerSource = powerSource;
    newVehicle.numberOfWheels = numberOfWheels;

    //Return the newly created instance.
    return newVehicle;
}

The factory method here initializes the object and sets up the properties. Since Vehicle has subclasses, you need to make sure you’re using [[self alloc] init] rather than [[Vehicle alloc] init]. That way, subclasses like Car can also use this inherited factory method to get Car objects back rather than Vehicle objects.

Note: Quality Coding has a great article How to Botch Your Objective-C Factory Method, that goes into a lot more depth on this subject.

Contributors

Over 300 content creators. Join our team.