Most object-oriented developers are familiar with static methods and classes. They are present in most programming languages including C# and Objective-C (where they are known as class methods). Here are some common cases in which they are used:
- Worker methods. Good for Simple calculations / processing, i.e
- Factory methods. Used to return pre-configured instances of a class, i.e.
- Singleton methods. Used to enforce a single global instance of a class, i.e.
- Global variables. Used to store configuration values, i.e.
Because static methods aren’t attached to an instance of an object, they are essentially “global code” that can be called from anywhere. Sounds great, right? That seems like it makes code “easier” to write, but it comes at the cost of design, maintainability, and testability. Static code violates the core principles of object-oriented programming (OOP), encourages tight coupling, and makes unit testing very difficult.
OOP groups code into classes that are instantiated as individual objects. Conversely, static methods are instructions that live in a single place in memory, just as in procedural programming. Further, calling a static method from a class inevitably creates a tight coupling between the two. Renaming or replacing the class containing static method necessarily requires refactoring all references to it. Finally, unit testing static methods is also practically impossible. Static methods cannot easily be swapped with mock implementations. In .NET it requires a tool like Microsoft Fakes, an advanced (and expensive) testing framework. It is a bit easier in Objective-C, where each class is itself instantiated as an object by the runtime and can be subclassed and overridden.
In Objective-C there is a definite over-reliance on static code in the form of the singleton pattern as a way of making classes globally accessible. “Globally accessible” should be a code smell to the experienced programmer. Instead of clearly delineated class relationships, singletons quietly result in a complex web of tightly-coupled dependencies. Such code is notoriously difficult to unit test, which is explains why it usually isn’t. Unfortunately, Apple’s own samples and documentation are full of this overused pattern (or rather, anti-pattern).
Static code is just one programming practice that makes code more difficult to design, maintain, and test, but there are others. In a future post I’ll go over why unit testing is so rare in Objective-C, and simple ways for iOS and OSX developers to recognize innocent practices that get in the way of testability. The good news is that “testable” code ultimately results in objectively “better” code, so it’s definitely worth striving for.