I wanted to ask you all for you opinions on code smells in Objective C, specifically Cocoa Touch. I'm working on a fairly complex game, and about to start the Great December Refactoring.
A good number of my classes, the models in particular, are full of methods that deal with internal business logic; I'll be hiding these in a private category, in my war against massive header files. Those private categories contain a large number of declarations, and this makes me feel uneasy... almost like Objective-C's out to make me feel guilty about all of these methods.
The more I refactor (a good thing!), the more I have to maintain all this duplication (not so good). It just feels wrong.
In a language like Ruby, the community puts a LOT of emphasis on very short, clear, beautiful methods. My question is, for Objective C (Cocoa Touch specifically), how long are your methods, how big are your controllers, and how many methods per class do you all find becomes typical in your projects? Are there any particularly nice, beautiful examples of Classes made up of short methods in Objective C, or is that simply not an important part of the language's culture?
DISCLOSURE: I'm currently reading "The Little Schemer", which should explain my sadness, re: Objective C.
Beauty is subjective. For me, an Objective-C class is beautiful if it is readable (I know what it is supposed to do) and maintainable (I can see what parts are responsible for doing what). I also don't like to be thrown out of reading code by an unfamiliar idiom. Sort of like when you are reading a book and you read something that takes you out of the immersion and reminds you that you are reading.
You'll probably get lots of different, mutually exclusive advice, but here are my thoughts.
- Nothing wrong with private methods being in a private category. That's what it is there for. If you don't like the declarations clogging up the file either use code folding in the IDE, or have your extensions as a category in a different file.
- Group related methods together and mark them with
#pragma mark
statements
- Whatever code layout you use, consistency is important. Take a few minutes and write your own guidelines (here are mine) so if you forget what you are supposed to be doing you have a reference.
- The controller doesn't have to be the delegate and datasource you can always have other classes for these.
- Use descriptive names for methods and properties. Yes, you may document them, but you can't see documentation when Xcode applies code completion, where well named methods and properties pay off. Also, code comments get stale if they aren't updated while the code itself changes.
- Don't try and write clever code. You might think that it's better to chain a sequence of method calls on one line, but the compiler is better at optimising than you might think. It's okay to use temporary variables to hold values (mostly these are just pointers anyway, so relatively small) if it improves readability. Write code for humans to read.
- DRY applies to Objective-C as much as other languages. Don't be worried about refactoring code into more methods. There is nothing wrong with having lots of methods as long as they are useful.
The very first thing I do even before implementing class or method is to ask: "How would I want to use this from the outside?"
I never ever, never begin by writing the internals of my classes and methods first. By starting of with an elegant public API the internals tend to become elegant for free, and if they don't then the ugliness is at least contained to a single method or class, and not allowed to pollute the rest of the code with it's smell.
There are many design patterns out there, two decades of coding have taught me that the only pattern that stand the test of time is: KISS. Keep It Simple Stupid.
Some general rules of thumb, for any language or environment:
- Follow your gut feeling over any advice you have read or heard!
- Bail out early!
- If needed, verify inputs early and bail out fast! Less cleanup to do.
- Never add something to your code that you do not use.
- An option for "reverse" might feel like something nice to have down the road.
- In that case add it down the road! Do not waste time adding complexity you do not need.
- Method names should describe what is done, never how it is done.
- Methods should be allowed to change their implementation without changing their name as long as the result is the same.
- If you can not understand what a method does from it's name then change the name!
- If the how part is complex enough, then use comments to describe your implementation.
- Do not fear the singletons!
- If your app only have one data model, then it is a singleton!
- Passing around a single variable all over the place is just pretending it is something else but a singleton and adding complexity as bonus.
- Plan for failures from the start.
- Always use for
doFoo:error
instead of doFoo:
from the start.
- Create nice
NSError
instances with end user readable localized descriptions from the start.
- It is a major pain to retrofit error handling/messages to a large existing app.
- And there will always be errors if you have users and IO involved!
- Cocoa/Objective-C is Object* Oriented, not **Class Oriented as most of the popular kids out there that claim to be OOP.
- Do not introduce a dumb value class with only properties, a class without methods performing actual work could just as well be a struct.
- Let your objects be intelligent! Why add a whole new
FooParser
class if a fooFromString:
method on Foo
is all you need?
- In Cocoa what you can do is always more important than what you are.
- Do not introduce a protocol if a target/action can do.
- Do not verify that instances conforms to protocols, is a kind of class, that is up to the compiler.
My 2 cents:
-
Properties are usually better than old-style getter+setter. Even if you use @dynamic properties - declare them with @property, this is way more informative and shorter.
- I personally don't simulate "private" methods for classes. Yes, I can write a category somewhere in the .m(m) file, but since Obj-C has no pure way to declare a private method - why should I invent one? Anyway, even if you really need something like that - declare a separate "MyClassPrivate.h" with a category and include it in the .m(m) files to avoid duplicating the declarations.
-
Binding. Binding for most Controller <-> UI relations, use transformers, formatters, just don't write methods to read/write controls values manually. It makes code look like something from MFC era.
-
C++, a lot of code look much better and shorter when written in C++. Since compiler understands C++ classes it's a good point for refactoring, especially when working will a low-level code.
- I usually split big controllers. Something more than 500 lines of code is a good candidate for refactoring for me. For instance, I have a document window controller, since some version of the app it extends with image importing/exporting options. Controller grows up to 1.000 lines where 1/2 is the "image stuff". That's a "trigger" for me to make an ImageStuffController, instantiate it in the NIB and put all image-relative code in there.
All above make it easier for me to maintain my code. For a huge projects, where splitting the controllers and classes to keep 'em small results big number of files, I usually try to extract some code into a framework. For example, if a big part of the app is communicating with external web-services, there is usually a straight way to extract a MyWebServices.framework from the main app.