intro to object-oriented design_ part 1_2 - ray wenderlich

22
Object-oriented programmers love vehicle hierarchies. Intro to Object-Oriented Design: Part 1/2 This post is also available in: Chinese (Simplified) , Korean A huge piece of the programming puzzle in working with Cocoa and Objective-C is Object-Oriented Programming. Almost all modern programming languages use this approach, and wrapping your head around its concepts and patterns can be incredibly helpful when reading and writing code. Underneath the relation between UITableView and UIScrollView or NSString and NSMutableString are the fundamental concepts of object-oriented design. By understanding these concepts you’ll have a better grasp on why things are organized the way they are in Cocoa and Cocoa Touch, and you’ll be a more thoughtful programmer when writing your own apps and frameworks. In this series you’ll learn more about object-oriented design, including the following concepts: The Basics Of Objects Inheritance Model-View-Controller Polymorphism Common Object-Oriented Patterns This series is designed for developers who are fairly new to programming overall – much like I was when I first started out. You likely haven’t worked with other languages extensively, and you’re not quite sure why everything is being done in a particular way. This tutorial will cover object-oriented design principles rather than specific syntax, so you should already know the basics of Objective-C and Xcode before reading on. If you need a refresher on the basics, check out our other Beginning Objective-C tutorials. Getting Started In order to try and understand some of these concepts in a more concrete manner, you’ll build an application called Vehicles. This uses one of the most common metaphors for translating real-world items into virtual objects: the “vehicle”, which could be a bicycle, a car, or really anything with wheels. For instance, this is a vehicle: Ellen Shapiro on November 14, 2013

Upload: mohit-pundir

Post on 15-Nov-2015

224 views

Category:

Documents


0 download

DESCRIPTION

Intro to Object-Oriented Design_ Part 1_2 - Ray Wenderlich

TRANSCRIPT

  • Object-oriented programmers love

    vehicle hierarchies.

    Intro to Object-Oriented Design: Part

    1/2

    This post is also available in: Chinese (Simplified), Korean

    A huge piece of the programming puzzle in working with Cocoa

    and Objective-C is Object-Oriented Programming. Almost all

    modern programming languages use this approach, and

    wrapping your head around its concepts and patterns can be

    incredibly helpful when reading and writing code.

    Underneath the relation between UITableView and

    UIScrollView or NSString and NSMutableString are the

    fundamental concepts of object-oriented design. By

    understanding these concepts youll have a better grasp on

    why things are organized the way they are in Cocoa and Cocoa

    Touch, and youll be a more thoughtful programmer when

    writing your own apps and frameworks.

    In this series youll learn more about object-oriented design,

    including the following concepts:

    The Basics Of Objects

    Inheritance

    Model-View-Controller

    Polymorphism

    Common Object-Oriented Patterns

    This series is designed for developers who are fairly new to programming overall much like I was when I first

    started out. You likely havent worked with other languages extensively, and youre not quite sure why everything is

    being done in a particular way.

    This tutorial will cover object-oriented design principles rather than specific syntax, so you should already know the

    basics of Objective-C and Xcode before reading on. If you need a refresher on the basics, check out our other

    Beginning Objective-C tutorials.

    Getting Started

    In order to try and understand some of these concepts in a more concrete manner, youll build an application called

    Vehicles. This uses one of the most common metaphors for translating real-world items into virtual objects: the

    vehicle, which could be a bicycle, a car, or really anything with wheels.

    For instance, this is a vehicle:

    Ellen Shapiro on November 14, 2013

  • But so is this:

    Or this:

  • Or this:

    In this part of the tutorial, youll create a data model using basic object-oriented techniques to represent all of these

    vehicles, and a simple application which implements the data model and displays vehicle data to the user.

    Download the starter project, which contains a basic framework for the application youll use to learn about Object-

    Oriented Programming.

    The Basics Of Objects

    In object-oriented programming, the basic goal is to break down the characteristics of a thing to create an object or

    objects that describe what that thing is and what that thing does.

    Sometimes, as with vehicles, your thing has a real-world equivalent. Sometimes it doesnt, as with the many

    different types of UIViewController objects. For the sake of simplicity, youll start out by creating objects that have

    real-world analogs.

    In order to answer the question of what a thing is, you have to first determine what its defining characteristics are.

    Other languages will refer to this as a field, a member, or even just a variable. However, in Objective-C the

    defining characteristics of an object are shown by its properties.

    Think about the generic concept of a vehicle for a moment something that describes all of the photos above.

    What common characteristics of a vehicle spring to mind?

    It has a number of wheels greater than zero.

    It has some sort of power source, whether human, gas, electric, or hybrid, which makes it move.

    It has a brand*, like Ford, Chevy, Harley-Davidson, or Schwinn.

    It has a model name like Mustang, Corvette, Sportster, or Fastback.

    It has a year associated with its manufacture.

    *- this is sometimes referred to in cars and trucks as a Make, but well refer to it as a brand across the board for

    clarity.

    Now that you have the basic characteristics of a vehicle, you can create an Object with these characteristics.

    There are two files in the starter project: Vehicle.h and Vehicle.m, which together represent a subclass of NSObject.

    In a moment youll read more about subclasses and what they are.

  • Add the following code to Vehicle.h after the @interface line:

    @property (nonatomic, assign) NSInteger numberOfWheels;@property (nonatomic, copy) NSString *powerSource;@property (nonatomic, copy) NSString *brandName;@property (nonatomic, copy) NSString *modelName;@property (nonatomic, assign) NSInteger modelYear;

    These property declarations describe the characteristics of the object you want to keep track of.

    A Minor Digression: Under The Hood With Properties

    Whenever you declare a @property in Xcode 4.4 and above, Xcode automatically synthesizes a backing instance

    variable, a getter method, and a setter method for that property. This saves a ton of boilerplate code. If there were no

    automatic synthesis, you would have to write all of the following code for every single instance variable:

    @interface Vehicle() { NSString *_brandName;}@end @implementation Vehicle //Setter method-(void)setBrandName:(NSString *)brandName{ _brandName = [brandName copy];} //Getter method-(NSString *)brandName{ return _brandName;} @end

    Not having to add this code for every single @property keeps your code cleaner and much more readable. This

    also means that you can access a @property in a couple of different ways:

    someVariableName = self.brandName; goes behind the scenes and calls the [self brandName];

    getter method that was synthesized for you, returns whatever value is stored in the _brandName instance

    variable, and assigns it to someVariableName.

    self.brandName = @"Some Brand Name"; also goes behind the scenes and calls the [self

    setBrandName:@"Some Brand Name"]; setter method, which in turn sets the value of the _brandName

    instance variable to @"Some Brand Name".

    Describing the Object

    On to the second critical question for every object what exactly does the object do?

    A programmatic description of what an object does is almost universally called a method. Think about the common

    actions of the vehicles in the photos above:

    It can go forward

    It can go backward

    It can stop

    It can turn

    It can change gears

  • It can make some sort of noise (e.g. a horn or a bell)

    Most often, youd be using methods with a void return type: for example, -(void)nameOfMethod. This is useful

    when you dont need to get any information back from the method and you simply want the method to execute.

    However, to make it a little easier to display whats happening in your app, youre going to use some methods that

    return NSString objects.

    A Minor Digression: Class Methods vs. Instance Methods

    Youve 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: Theres 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 wouldnt make any

    sense since thats just the blueprint for a string theres 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, Heres what Im going to

    be able to do, but it doesnt give any details of how its 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; youll 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, youll call each of the instance methods and log the output to see what

    they do.

    Build and run your app; youll see that all implemented strings return proper logs with the appropriate items filled in.

    But anywhere a property isnt set or a method returns nil, youll see a (null), as follows:

    Youll 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, its 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. Its the parent class to

    almost all of the everyday Objective-C classes.

    Note: There are some C structs like CGRect and CGSize that dont use NSObject as their parent class, since

    structs dont 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 Apples 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:

    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 didnt have to do anything extra to access the numberOfWheels property of the Vehicle parent

    class? Thats because by inheriting from the Vehicle class, Car already knows about all of Vehicles 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 youve 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] theres

    some important stuff in there that needs to execute before your UIViewController subclass can do its work.

    However, since most of the methods youre going to override in the Car class are returning nil, you can just create

    your own implementations. Theres nothing useful in the superclasss implementation so theres 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 dont need to be started, but cars do! In this case, youre 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 cant see it, that doesnt protect a

    subclass from overriding the method. You really cant stop something from going wrong in this case, but you

    should make notes in your apps 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.

    Building out the User Interface

    In VehicleListTableViewController.m, add the following import to the top of the file, just below the import for Vehicle:

    #import "Car.h"

    Next, add the following method between didReceiveMemoryWarning and #pragma mark - Table View:

    #pragma mark - Data setup-(void)setupVehicleArray{ //Create a car. Car *mustang = [[Car alloc] init]; mustang.brandName = @"Ford"; mustang.modelName = @"Mustang"; mustang.modelYear = 1968; mustang.isConvertible = YES; mustang.isHatchback = NO; mustang.hasSunroof = NO; mustang.numberOfDoors = 2; mustang.powerSource = @"gas engine"; //Add it to the array [self.vehicles addObject:mustang]; //Create another car. Car *outback = [[Car alloc] init]; outback.brandName = @"Subaru"; outback.modelName = @"Outback"; outback.modelYear = 1999; outback.isConvertible = NO; outback.isHatchback = YES; outback.hasSunroof = NO; outback.numberOfDoors = 5; outback.powerSource = @"gas engine"; //Add it to the array. [self.vehicles addObject:outback]; //Create another car Car *prius = [[Car alloc] init]; prius.brandName = @"Toyota"; prius.modelName = @"Prius"; prius.modelYear = 2002; prius.hasSunroof = YES; prius.isConvertible = NO; prius.isHatchback = YES; prius.numberOfDoors = 4; prius.powerSource = @"hybrid engine";

  • //Add it to the array.

    [self.vehicles addObject:prius]; //Sort the array by the model year NSSortDescriptor *modelYear = [NSSortDescriptor sortDescriptorWithKey:@"modelYear" ascending:YES]; [self.vehicles sortUsingDescriptors:@[modelYear]];}

    This simply separates the data setup methods and adds the method to construct your vehicle array.

    Find awakeFromNib and add this code to the end of the method:

    // Initialize the vehicle array self.vehicles = [NSMutableArray array]; // Call the setup method [self setupVehicleArray]; // Set the title of the View Controller, which will display in the Navigation bar. self.title = @"Vehicles";

    The above method executes when your Storyboard finishes constructing the nib for a particular UIViewController at

    runtime. It calls the setupVehicleArray method you just created, and sets the title of the

    VehicleListTableViewController to reflect its contents.

    Build and run your application; youll see something that looks like the following:

    The numbers you see represent memory addresses and will be different, but everything else should look the same.

    The good news is that these objects are being recognized as Car objects. The bad news is that whats being

    displayed isnt terribly useful. Take a look at whats set up in the UITableViewDataSource method

    tableView:cellForRowAtIndexPath:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; Vehicle *rowVehicle = self.vehicles[indexPath.row]; cell.textLabel.text = [rowVehicle description];

  • return cell;}

    Here, youre grabbing a UITableViewCell object then getting the Vehicle at the index in the self.vehicles array

    matching the row of the cell youre constructing. Next, you set that specific Vehicles description string as the text

    for the cells textLabel.

    The string produced by the description method (which is inherited from NSObject) isnt very human-friendly. Youll

    want to define a method in Vehicle that describes what each Vehicle object represents in a way that is easy for your

    users to understand.

    Go back to Vehicle.h and add the following new method declaration, below all the other basic method declarations,

    but above @end:

    //Convenience method for UITableViewCells and UINavigationBar titles.-(NSString *)vehicleTitleString;

    Then, in Vehicle.m, add the following implementation, again below the basic method implementations:

    #pragma mark - Convenience Methods-(NSString *)vehicleTitleString{ return [NSString stringWithFormat:@"%d %@ %@", self.modelYear, self.brandName, self.modelName];}

    The method above takes three properties that should be used on every single Vehicle and uses them to describe

    the vehicle concisely.

    Now, update VehicleListTableViewControllers tableView:cellForRowAtIndexPath: method to use this new

    method on Vehicle as follows:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; Vehicle *rowVehicle = self.vehicles[indexPath.row]; cell.textLabel.text = [rowVehicle vehicleTitleString]; return cell;}

    Build and run your application; it should look a little nicer now:

  • However, if you select a Vehicle from the list, all youll see are the same elements visible in the storyboard, with

    none of the details from the Vehicle you selected:

    Whats up with that?

    Open VehicleDetailViewController.m; youll see that while the UI was constructed in the Storyboard and all the

    IBOutlets were already hooked up to save you some time fighting with AutoLayout, none of the data is hooked up.

    Note: Youll notice that several of the IBOutlets are set up in the VehicleDetailViewController.m instead of in

    the .h file, as they normally would be.

    If you have properties you dont want to be publicly available to other classes, you can always add them to your

    .m file in the private implementation. This is the @interface thats declared at the top of a .m file and noted by

    the parentheses after the class name. For example, UIViewController() would be the private

    implementation of UIViewController.

    Any @property declared in that interface can still be accessed as an IBOutlet (if properly annotated as

  • such) in your Storyboard and within your .m implementation file, but it wont be accessible to any unrelated

    classes or to subclasses of your class.

    Hooking Your Data to Your View

    To hook up the data, update configureView in VehicleDetailViewController.m to take advantage of the vehicle set

    by the segue as follows:

    - (void)configureView{ // Update the user interface for the detail vehicle, if it exists. if (self.detailVehicle) { //Set the View Controller title, which will display in the Navigation bar. self.title = [self.detailVehicle vehicleTitleString]; //Setup the basic details string based on the properties in the base Vehicle class. NSMutableString *basicDetailsString = [NSMutableString string]; [basicDetailsString appendString:@"Basic vehicle details:\n\n"]; [basicDetailsString appendFormat:@"Brand name: %@\n", self.detailVehicle.brandName]; [basicDetailsString appendFormat:@"Model name: %@\n", self.detailVehicle.modelName]; [basicDetailsString appendFormat:@"Model year: %d\n", self.detailVehicle.modelYear]; [basicDetailsString appendFormat:@"Power source: %@\n", self.detailVehicle.powerSource]; [basicDetailsString appendFormat:@"# of wheels: %d", self.detailVehicle.numberOfWheels]; self.vehicleDetailsLabel.text = basicDetailsString; }}

    Build and run your application; select a vehicle from the TableView and youll see that the detail view is now showing

    the correct title and details, as so:

    Model-View-Controller Encapsulation of Logic

  • iOS and many other modern programming languages have a design pattern known as Model-View-Controller , or

    MVC for short.

    The idea behind MVC is that views should only care about how they are presented, models should only care about

    their data, and controllers should work to marry the two without necessarily knowing too much about their internal

    structure.

    The biggest benefit to the MVC pattern is making sure that if your data model changes, you only have to make

    changes once.

    One of the biggest rookie mistakes that programmers make is putting far too much of the logic in their

    UIViewController classes. This ties views and UIViewControllers too closely to one particular type of model,

    making it harder to reuse views to display different kinds of details.

    Why would you want to do implement the MVC pattern in your app? Well, imagine that you wanted to add more

    specific details about a car to VehicleDetailViewController. You could start by going back into configureView and

    adding some information specifically about the car, as so:

    //Car-specific details[basicDetailsString appendString:@"\n\nCar-Specific Details:\n\n"];[basicDetailsString appendFormat:@"Number of doors: %d", self.detailVehicle.numberOfDoors];

    But youll notice that theres one minor problem with this:

    VehicleDetailsViewController only knows about the properties of the main Vehicle superclass; it doesnt know

    anything anything about the Car subclass.

    There are a couple of ways you can fix this.

    The one that seems the most immediately obvious is to just import Car.h, so that VehicleDetailViewController

    knows about the properties of the Car subclass. But that would mean having to add a ton of logic to handle all the

    properties for every single subclass.

    Any time you catch yourself doing that, ask yourself: is my view controller trying to do too much?

    In this case, the answer is yes. You can take advantage of inheritance to use the same method to supply the string

    to be displayed for the appropriate details for each subclass.

    Creating Subclasses via Inheritance

    First, add the following new method to Vehicle.h:

    //Convenience method to get the vehicle's details.-(NSString *)vehicleDetailsString;

    Thats the publicly-declared method that can be used from clients such as VehicleDetailsViewController.

    Theres no need for them to know about each individual property; instead, they can just ask for

    vehicleDetailsString, receive a fully-formatted string, and use it.

    Switch over to Vehicle.m and add the implementation:

    -(NSString *)vehicleDetailsString{ //Setup the basic details string based on the properties in the base Vehicle class. NSMutableString *basicDetailsString = [NSMutableString string]; [basicDetailsString appendString:@"Basic vehicle details:\n\n"]; [basicDetailsString appendFormat:@"Brand name: %@\n", self.brandName]; [basicDetailsString appendFormat:@"Model name: %@\n", self.modelName]; [basicDetailsString appendFormat:@"Model year: %d\n", self.modelYear];

  • [basicDetailsString appendFormat:@"Power source: %@\n", self.powerSource]; [basicDetailsString appendFormat:@"# of wheels: %d", self.numberOfWheels]; return [basicDetailsString copy];}

    The method is similar to what you added to VehicleDetailViewController.m, except it returns the generated string

    rather than display it somewhere directly.

    Now, you can use inheritance to take this basic vehicle string and add the specific details for the Car class. Open

    Car.m and add the override implementation of vehicleDetailsString:

    - (NSString *)vehicleDetailsString{ //Get basic details from superclass NSString *basicDetails = [super vehicleDetailsString]; //Initialize mutable string NSMutableString *carDetailsBuilder = [NSMutableString string]; [carDetailsBuilder appendString:@"\n\nCar-Specific Details:\n\n"]; //String helpers for booleans NSString *yes = @"Yes\n"; NSString *no = @"No\n"; //Add info about car-specific features. [carDetailsBuilder appendString:@"Has sunroof: "]; if (self.hasSunroof) { [carDetailsBuilder appendString:yes]; } else { [carDetailsBuilder appendString:no]; } [carDetailsBuilder appendString:@"Is Hatchback: "]; if (self.isHatchback) { [carDetailsBuilder appendString:yes]; } else { [carDetailsBuilder appendString:no]; } [carDetailsBuilder appendString:@"Is Convertible: "]; if (self.isConvertible) { [carDetailsBuilder appendString:yes]; } else { [carDetailsBuilder appendString:no]; } [carDetailsBuilder appendFormat:@"Number of doors: %d", self.numberOfDoors]; //Create the final string by combining basic and car-specific details. NSString *carDetails = [basicDetails stringByAppendingString:carDetailsBuilder]; return carDetails;}

    Cars version of the method starts by calling the superclasss implementation to get the vehicle details. It then

    builds the car-specific details string into carDetailsBuilder and then combines the two at the very end.

    Now replace configureView in VehicleDetailViewController.m with the much simpler implementation below to

    display this string that youve created:

    - (void)configureView{ // Update the user interface for the detail vehicle, if it exists. if (self.detailVehicle) {

  • //Set the View Controller title, which will display in the Navigation bar. self.title = [self.detailVehicle vehicleTitleString]; self.vehicleDetailsLabel.text = [self.detailVehicle vehicleDetailsString]; }}

    Build and run your application; select one of the cars, and you should now be able to see both general details and

    car-specific details as shown below:

    Your VehicleDetailViewController class now allows the Vehicle and Car classes to determine the data to be

    displayed. The only thing ViewController is doing is connecting that information up with the view!

    The real power in this is evident when you create further subclasses of Vehicle. Start with a fairly simple one for a

    motorcycle.

    Go to File\New\File\CocoaTouch\Objective-C Class, and create a new subclass of Vehicle called Motorcycle.

    Since motorcycles can have either a deep booming engine noise, or a high-pitched whine of an engine noise, each

    Motorcycle object you create you should specify which type of noise it makes.

    Add a single string property for the Engine Noise in Motorcycle.h just after the @interface line:

    @property (nonatomic, strong) NSString *engineNoise;

    Then, open Motorcycle.m. Add the following init method:

    #pragma mark - Initialization- (id)init{ if (self = [super init]) { self.numberOfWheels = 2; self.powerSource = @"gas engine"; } return self;}

    Since all motorcycles have two wheels and are gas-powered (for the sake of this example, anything thats electric-

    powered would be considered a scooter, not a motorcycle), you can set up the number of wheels and power source

    when the object is instantiated.

    Next, add the following methods to override all the superclass methods that will just return nil otherwise:

  • #pragma mark - Superclass Overrides-(NSString *)goForward{ return [NSString stringWithFormat:@"%@ Open throttle.", [self changeGears:@"Forward"]];} -(NSString *)goBackward{ return [NSString stringWithFormat:@"%@ Walk %@ backwards using feet.", [self changeGears:@"Neutral"], self.modelName];} -(NSString *)stopMoving{ return @"Squeeze brakes.";} -(NSString *)makeNoise{ return self.engineNoise;}

    Finally, override the vehicleDetailsString method in order to add the Motorcycle-specific details to the

    vehicleDetailsString as shown below:

    - (NSString *)vehicleDetailsString{ //Get basic details from superclass NSString *basicDetails = [super vehicleDetailsString]; //Initialize mutable string NSMutableString *motorcycleDetailsBuilder = [NSMutableString string]; [motorcycleDetailsBuilder appendString:@"\n\nMotorcycle-Specific Details:\n\n"]; //Add info about motorcycle-specific features. [motorcycleDetailsBuilder appendFormat:@"Engine Noise: %@", self.engineNoise]; //Create the final string by combining basic and motorcycle-specific details. NSString *motorcycleDetails = [basicDetails stringByAppendingString:motorcycleDetailsBuilder]; return motorcycleDetails;}

    Now, its time to create some instances of Motorcycle.

    Open VehicleListTableViewController.m and make sure it can see the Motorcycle class by adding the following

    import:

    #import "Motorcycle.h"

    Next, find setupVehicleArray and add the following code below the Cars youve already added but above where

    you sort the array:

    //Create a motorcycle Motorcycle *harley = [[Motorcycle alloc] init]; harley.brandName = @"Harley-Davidson"; harley.modelName = @"Softail"; harley.modelYear = 1979; harley.engineNoise = @"Vrrrrrrrroooooooooom!"; //Add it to the array. [self.vehicles addObject:harley];

  • //Create another motorcycle

    Motorcycle *kawasaki = [[Motorcycle alloc] init]; kawasaki.brandName = @"Kawasaki"; kawasaki.modelName = @"Ninja"; kawasaki.modelYear = 2005; kawasaki.engineNoise = @"Neeeeeeeeeeeeeeeeow!"; //Add it to the array [self.vehicles addObject:kawasaki];

    The above code simply instantiates two Motorcycle objects and adds them to the vehicles array.

    Build and run your application; youll see the two Motorcycle objects you added in the list:

    Tap on one of them, and youll be taken to the details for that Motorcycle, as shown below:

    Whether its a car or a motorcycle (or even a plain old vehicle), you can call vehicleDetailsString and get the

    relevant details.

  • By using proper separation between the model, the view, and the view controller and inheritance, youre now able to

    display data for several subclasses of the same superclass without having to write tons of extra code to handle

    different subclass types. Less code written == happier developers! :]

    Housing Logic in the Model Class

    You can also use this approach to keep some of the more complicated logic encapsulated within the model class.

    Think about a Truck object: many different types of vehicles are referred to as a truck, ranging from pickup trucks to

    tractor-trailers. Your truck class will have a little bit of logic to change the trucks behavior based on the number of

    cubic feet of cargo it can haul.

    Go to File\New\File\CocoaTouch\Objective-C Class, and create a new subclass of Vehicle called Truck.

    Add the following integer property to Truck.h to hold the trucks capacity data:

    @property (nonatomic, assign) NSInteger cargoCapacityCubicFeet;

    Since there are so many different types of trucks, you dont need to create an init method that provides any of those

    details automatically. You can simply override the superclass methods which would be the same no matter the size

    of the truck.

    Open Truck.m and add the following methods:

    #pragma mark - Superclass overrides- (NSString *)goForward{ return [NSString stringWithFormat:@"%@ Depress gas pedal.", [self changeGears:@"Drive"]];} - (NSString *)stopMoving{ return [NSString stringWithFormat:@"Depress brake pedal. %@", [self changeGears:@"Park"]];}

    Next, youll want to override a couple of methods so that the string returned is dependent on the amount of cargo the

    truck can haul. A big truck needs to sound a backup alarm, so you can create a private method (i.e., a method not

    declared in the .h file and thus not available to other classes) to do this.

    Add the following helper method code to Truck.m:

    #pragma mark - Private methods- (NSString *)soundBackupAlarm{ return @"Beep! Beep! Beep! Beep!";}

    Then back in the superclass overrides section, you can use that soundBackupAlarm method to create a

    goBackward method that sounds the alarm for big trucks:

    - (NSString *)goBackward{ NSMutableString *backwardString = [NSMutableString string]; if (self.cargoCapacityCubicFeet > 100) { //Sound a backup alarm first [backwardString appendFormat:@"Wait for \"%@\", then %@", [self soundBackupAlarm], [self changeGears:@"Reverse"]]; } else { [backwardString appendFormat:@"%@ Depress gas pedal.", [self changeGears:@"Reverse"]]; } return backwardString;

  • }

    Trucks also have very different horns; a pickup truck, for instance, would have a horn similar to that of a car, while

    progressively larger trucks have progressively louder horns. You can handle this with some simple if/else

    statements in the makeNoise method.

    Add makeNoise as follows:

    - (NSString *)makeNoise{ if (self.numberOfWheels 4 && self.numberOfWheels

  • eighteenWheeler.modelName = @"579"; eighteenWheeler.modelYear = 2013; eighteenWheeler.numberOfWheels = 18; eighteenWheeler.cargoCapacityCubicFeet = 408; eighteenWheeler.powerSource = @"diesel engine"; //Add it to the array [self.vehicles addObject:eighteenWheeler];

    This will create a couple of Truck objects with the truck-specific properties to join the cars and motorcycles in the

    array.

    Build and run you application; select one of the Trucks to verify that you can now see the appropriate Truck-specific

    details, as demonstrated below:

    That looks pretty great! The truck details are coming through thanks to the vehicleDetailsString method,

    inheritance, and overridden implementations.

    Where To Go From Here?

    You can download a copy of the project up to this point.

    Youve set up a base Vehicle class with Car, Motorcycle, and Truck subclasses, all listed in the same table view.

    However, theres no way to verify that all of your trucks size-specific handling is working correctly.

    Part 2 of this tutorial will fill out the rest of the app to display more vehicle details. Along the way, youll learn about

    polymorphism and a couple of additional major design patterns for Object-Oriented Programming.

    Until then, why not try implementing a Bicycle class or a few more custom properties to some of the vehicle

    subclasses? Or you can read up on Apples reference guide Object-Oriented Programming with Objective-C.

    Got questions? Ask away in the comments below!

  • designatednerd

    Ellen Shapiro is a mobile developer in Chicago, Illinois who builds iOS and Android

    apps for Vokal Interactive, and is working in her spare time to help bring Hum to life.

    Shes also developed several independent applications through her personal

    company, Designated Nerd Software.

    When she's not writing code, she's usually tweeting about it.