Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you keep Cocoa controllers from getting too big?

Do you have some tricks or techniques to break Cocoa controller classes into smaller chunks? I find that whatever I do the controllers end up being one of the more complicated classes in my design. The basic stuff is simple, but once I have several pop-overs or action sheets running, things get uncomfortably complex. It's not that bad, but still I would like to refactor the code into several standalone parts.

I thought about categories, but the code is not that independent (a lot of times it needs to tap into viewWillAppear, for example) and I find that I spend a long time fighting the compiler. I also thought about adding functionality in layers using inheritance, but that feels like a hack.

like image 927
zoul Avatar asked May 18 '10 18:05

zoul


2 Answers

The issue is not size, but responsibility. Is your controller wearing more than one hat? If so, blow it up into multiple, one-job-per-class controllers.

Categories help with size, but not responsibility. If you're still doing multiple things in one (amalgamated) class, then you still have a problem; moving them into separate files did not solve it.

Having many categories on a single class brings a risk of method collisions: implementing the same method in multiple categories, probably by implementing it in category B while forgetting that category A already has one. This will cause a problem when the object sends itself a message, expecting one category's response to that message and getting the other's.

Declaring all of the categories in the main class header mitigates that risk, as you can see that another category already has a method by the name you're about to enter. However, every method you add, thereby lengthening the header file, mitigates the mitigation.

If your controller is wearing more than one hat, blow it up into multiple classes.

I recommend Martin Fowler's book “Refactoring”. Refactoring your code is cleaning it up, and blowing up too-big classes (and methods and functions) is a subset of such clean-up.

Of course, multiple classes that once were one need a replacement for the communication that was previously internal within the class. Cocoa provides a number of solutions:

  • Accessors
  • Delegation
  • Notifications
  • Key-Value Observing
  • Target-action
  • Blocks

You don't need to pick exactly one, nor do you need to use them all. Which solutions are appropriate will depend on exactly what communication needs your new classes will have with each other.

like image 144
Peter Hosey Avatar answered Dec 03 '22 17:12

Peter Hosey


Categories is the way to go. The trick (for your "not that independent" stuff) is to declare the category methods in your main controller .h file (not a separate controller+category.h file, there will be none) then implement them in your controller+category.m file. Like this:

//******************
// MyController.h
//******************
#import <Cocoa/Cocoa.h>

@interface MyContoller : NSObject 
{
    NSWindow   *window;

    // stuff to implement in the category
    NSComboBox *someCombo;
    NSButton       *someButton; 
}

@property IBOutlet NSWindow   *window;

@property IBOutlet NSComboBox *someCombo;
@property IBOutlet NSButton   *someButton;

@end

@interface  MyController (MyCategory)

- (IBAction)someComboSelected:(id)sender;
- (IBAction)someButtonPressed:(id)sender;

@end

//**************************
// MyController+MyCategory.m
//**************************
#import <Cocoa/Cocoa.h>
#import "MyController.h"

@implementation MyContoller (MyCategory)

- (IBAction)someComboSelected:(id)sender
{
    ....
}

- (IBAction)someButtonPressed:(id)sender
{
    ....
}

Obviously, I didn't include "MyController.m", where you put the "@synthesize" stuff and whatever else is needed for the main controller/awake-from-nib/whatever. Anyway, doing it this way gives your controller methods access to the category methods and vise-versa, and the category methods have access to all the properties.

like image 23
obijohn Avatar answered Dec 03 '22 17:12

obijohn