Object Oriented Programming Best Practices |
| Interface with Interfaces Use interfaces as the glue throughout your code instead of classes: define interfaces to describe the exterior of objects (i.e. their Type) and type all variables, parameters, and return values to interfaces. The most important reason to do this is that interfaces focus on the clients needs: interfaces define what functionality a client will receive from an Object without coupling the client to the Objects implementation. This is one of the core concepts to OO. |
| Create Different Interfaces for Different Types of Clients Provide different interfaces to support different types of clients and to prevent exposing responsibilities to clients who should not see it. Provides a more understandable system for a particular clients perspective and makes maintenance impacts more visible. |
| Use Interfaces over Abstract Classes If you can conceive of someone else implementing a class's functionality differently, define an interface, not an abstract class. Generally, use abstract classes only when they are ``partially abstract''; i.e., they implement some functionality that must be shared across all subclasses. Interfaces are more flexible than abstract classes. They support multiple inheritance and can be used as `mixins' in otherwise unrelated classes. |
| Rarely declare a class final Declare a class as final only if it is a subclass or implementation of a class or interface declaring all of its non-implementation-specific methods. (And similarly for final methods). Making a class final means that no one ever has a chance to reimplement functionality. Defining it instead to be a subclass of a base that is not final means that someone at least gets a chance to subclass the base with an alternate implementation. Which will essentially always happen in the long run. |
| Minimize statics (except for static final constants). Static variables act like globals in non-OO languages. They make methods more context-dependent, hide possible side-effects, sometimes present synchronized access problems. and are the source of fragile, non-extensible constructions. Also, neither static variables nor methods are overridable in any useful sense in subclasses. |
| Use Factories for Creating Objects Use Factories and Factory methods for "public" object construction: have an object be responsible for construction instead of having clients directly call "new AClass()". The reason to use factory creation methods instead of straight constructors is because they: Allow more flexibility in "creating" a new object: the implementation can just reuse an existing object if the semantics make sense. Can have better names: "newTimeNow()" and "newTimeFromSeconds(...)" instead of "new Time()" and "new Time(...)" Provide better separation between interface and implementation: we can document the factory method in an interface. Naturally flow into more sophisticated factory designs.The implementation can take advantage of inheritance since it is a "normal" object method. |
| Never declare instance variables as public. The standard OO reasons. Making variables public gives up control over internal class structure. Also, methods cannot assume that variables have valid values. |
| All data should be hidden within its class. |
| Keep things as private as possible. Once you publicize an aspect of your library (a method, a class, a field), you can never take it out. If you do, youll wreck somebodys existing code, forcing them to rewrite and redesign. If you publicize only what you must, you can change everything else with impunity, and since designs tend to evolve this is an important freedom. In this way, implementation changes will have minimal impact on derived classes. Privacy is especially important when dealing with multithreadingonly private fields can be protected against un-synchronized use. |
| Users of a class must be dependent on its public interface, but a class should not be dependent on its users. |
| Do not change the state of an object without going through its public interface. |
| Minimize the number of messages in the protocol of a class. |
| Do not clutter the public interface of a class with things that users of that class are not able to use or are not interested in using. |
| Keep related data and behavior in one place. Spin off non-related information into another class. |
| Distribute system intelligence horizontally as uniformly as possible, i.e. the top level classes in a design should share the work uniformly. |
| Do not create god classes/objects in your system. Be very suspicious of an abstraction whose name contains Driver, Manager, System, or Subsystem. |
| In applications which consist of an object-oriented model interacting with a user interface, the model should never be dependent on the interface. The interface should be dependent on the model. |
| Inheritance should only be used to model a specialization hierarchy. |
| All data in a base class should be private, i.e. do not use protected data. |
| Theoretically, inheritance hierarchies should be deep, i.e. the deeper the better. Pragmatically, inheritance hierarchies should be no deeper than an average person can keep in their short term memory. A popular value for this depth is six. |
| All abstract classes must be base classes. All base classes should be abstract classes. |
| Factor the commonality of data, behavior, and/or interface as high as possible in the inheritance hierarchy. |
| If two or more classes only share common data (no common behavior) then that common data should be placed in a class which will be contained by each sharing class. |
| If two or more classes have common data and behavior (i.e. methods) then those classes should each inherit from a common base class which captures those data and methods. |
| If two or more classes only share common interface (i.e. messages, not methods) then they should inherit from a common base class only if they will be used polymorphically. |
| If you have an example of multiple inheritance in your design, assume you have made a mistake and prove otherwise. |
| Whenever there is inheritance in an object-oriented design ask yourself two questions: 1) Am I a special type of the thing I'm inheriting from? and 2) Is the thing I'm inheriting from part of me? |
| Whenever you have found a multiple inheritance relationship in a object-oriented design be sure that no base class is actually a derived class of another base class, i.e. accidental multiple inheritance. |
| What are some alternatives to inheritance? Delegation is an alternative to inheritance. Delegation means that you include an instance of another class as an instance variable, and forward messages to the instance. It is often safer than inheritance because it forces you to think about each message you forward, because the instance is of a known class, rather than a new class, and because it doesn't force you to accept all the methods of the super class: you can provide only the methods that really make sense. On the other hand, it makes you write more code, and it is harder to re-use (because it is not a subclass). |
| The Utility Class idiom A utility class contains only public static methods and is not meant to be instantiated. Utility classes are easily abused and can lead to procedural programming. However, Bloch pointed out two valid uses of this idiom. You should use a utility class to act on collections of primitives and collections of objects that implement interfaces. java.util.Collections
serves as an example of a properly designed utility class. |
| Watch for long method definitions. Methods should be brief, functional units that describe and implement a discrete part of a class interface. A method that is long and complicated is difficult and expensive to maintain, and is probably trying to do too much all by itself. If you see such a method, it indicates that, at the least, it should be broken up into multiple methods. It may also suggest the creation of a new class. Small methods will also foster reuse within your class. (Sometimes methods must be large, but they should still do just one thing.) |
| Return Immutables From Accessor Methods Many accessor methods need to return values which are references to objects which represent a part of the state of the called object; directly returning the state in this form will expose the internals of the called object, and the caller will have the ability to modify that state unpredictably. |
| Copy Mutable Parameters If an object has to use a mutable object as a ValueObject, operations on the mutable value can change the object's internal state without its knowledge and can cause it's state to become inconsistent. Therefore: an object that holds mutable objects as state must ReturnNewObjectsFromAccessorMethods. Additionally, it must make private copies of mutable objects that are passed to it and that it needs to store internally. (by making use of use cloning) |
| Object Pooling Don't do it. Outside of some cases where you are actually pooling the resource held by the object and not the object itself (e.g. thread pools, database connections, huge bitmaps, etc.), object pooling is not worth doing anymore. Object allocation is now so fast that the necessary synchronization required by an object pool implementation is slower than simply allocating new objects. |
| Choose interfaces over abstract classes. If you know something is going to be a
base class, your first choice should be to make it an interface, and only if youre
forced to have method definitions or member variables should you change it to an abstract
class. An interface talks about what the client wants to do, while a class tends to focus
on (or allow) implementation details. |