For the last few posts, we’ve looked at naming things. (If you missed them, here are the links to Part 1 and Part 2.) In this post we’re going to look at class and interface naming.
We’ve seen that naming things is very important. It forces us to think hard about what the item is and does. A good name suggests the item’s functionality. The name also brings with it a whole set of assumptions and expectations.
Good names can show that similar things are connected, and that dissimilar things are separate. Sometimes the quality of a name isn’t obvious until we try and name something similar. If the original name is good, we can easily derive a name for the new, similar thing. A name is good if it survives its future usage.
The longer an item is in scope (i.e. used and visible), the more important it is to have a good name for that item. Classes and interfaces are the building blocks for our design, and are used more widely and are in scope for longer than variables of those types.
Class Names
Generally, we’ll get a good “gut” feeling about an appropriate class name. It should be specific enough to focus our thoughts (Single Responsibility principle), but general enough to allow easy extension and specialisation (Open-Closed principle).
The names we chose for classes should be concise, informative, and easy to remember. They should be meaningful in the context of the problem. Names should be easy to pronounce. They shouldn’t consist of abbreviations or contain digits (if possible).
Type names for classes should indicate the functionality of the class without us needing to read the documentation or work through the source code. Good names can show patterns in the code.
If there is a class in a library called ImageRenderer
, it’s very obvious that we wouldn’t use that class to make a connection to an HTTP server. We wouldn’t even have to look it up in the documentation. Conversely, if we came across a class called Class153A
, we wouldn’t have a clue what it could possibly be used for.
Class names are generally nouns or noun phrases, such as Employee
, Account
, Invoice
or ArrayList
. This distinguishes type names from methods, which are named with verb phrases.
Interface names are sometimes also nouns or noun phrases such as Collection
, Map
and List
, but are more often adjectives or adjective phrases, e.g., Cloneable
, Serializable
and Runnable
.
We should beware of class names that are verbs or derived from verbs. This often shows that we’re using the class in place of a method of a yet-undiscovered class.
We should choose a convention for class names and stick to it. This gives internal code consistency. Many programming languages use the convention of camel case naming.
Code To An Interface, Not An Implementation
One of the most important principles in OO development is to code to an interface, not an implementation.
This is the concept of an abstract API that is independent of any specific implementation. This helps decouple (separate) our code from other parts of the code base.
If we express our business logic entirely in terms of how the code works with interfaces, we can substitute any concrete class implementation with any other concrete implementation of the same interface without changing any business logic. This is the Liskov Substitution principle at work. This means that we will only ever have interfaces injected as dependencies.
This then leads to interfaces being the most important building blocks for us to communicate our intent to the system, and on which to model our classes.
If we look at the following code, what do we know about Message
? Is it an interface? Is it a class? Does it matter? Should we even care?
public void process(Message message) {
// appropriate code
}
If our naming conventions don’t distinguish between interfaces and classes, our code doesn’t need to care!
We should be programming in terms of interfaces anyway, so name the interfaces as the base concept. In the above example, we’d code Message
as an interface. After that, we would create concrete classes implementing the Message
interface. These would have names such as BytesMessage
, StreamMessage
, TextMessage
, etc.
It’s best to stick to these constructs even though the classes won’t group nicely when sorted alphabetically.
The Do’s and Don’t’s of Prefixes
Delphi had the coding convention of prefixing all class names with a “T” to show that it was a type definition. TObject
was the base class of all objects and components. The same idea was applied to interfaces too. The “I” prefix was used. IInterface
was the base class for all interfaces. C## uses the “I” prefix for interfaces as well.
I don’t like and won’t recommend the use of these prefixes. Class names should be clean without any clutter. The concrete implementation should tell us why it is special.
For example, a name like User
is much easier to read and remember than IUser
or TUser
. The concept of a User
should be coded as an interface. If we have a concrete implementation of a User
, the name of that implementation should tell us what is special about it. Is it a user whose data is stored on a server? Call it ServerUser
. Do we store the user data locally? Call it LocalUser
.
Another example: if we have a concept of a notification in our system, we should create an interface called Notification
which specifies its behaviour. We can then create concrete implementation classes as needed. These could be called SmsNotification
, EmailNotification
, PopUpNotification
, etc. These class names tell us what the differences are, and give an indication of where and how we will use them.
While on personal preferences, I do like prefixing abstract classes with Abstract
. Using the Abstract
prefix makes it clear to the programmer that this is an abstract class with data fields and reusable implemented code. Abstract classes make good base classes for extension. The standard Java library has AbstractCollection
, AbstractList
, AbstractMap
, etc. Spring uses the prefix extensively as well.
Conclusion
Remember that there are no hard and fast rules that will miraculously make the job of naming things simple. But by keeping the previous conventions, hints and ideas in mind when we have to think of good names will definitely make our programming life a lot easier.
Decide on a standard, be consistent and stick with it. We’ll never get universal agreement on how to name things, so we shouldn’t lose too much sleep about it.
Code is literature, so we must make it as humanly readable as possible!
I look forward to reading your comments.
Until then, stay safe and write good literary code!