Encouraging single responsibility with a naming convention

The single responsibility principle tells us that each class should do only one thing. This is easy to forget or ignore when under pressure; getting it done trumps getting it right. We can reinforce our dedication to this SOLID principle with good habits, such as a class and method naming convention. There are two types of class that need useful, descriptive names.

  1. Model classes that hold state, such as data loaded from a database.

  2. Service classes that change the state, e.g. code that manipulates data in a database.

Model classes are easy to name because they represent business entities, like users, invoices and subscriptions. In these examples, the names are User, Invoice, and Subscription. Service class names are more open to interpretation with some poorly named, real-world examples being UserOperations and FinanceAdmin. And some terrible, real-world examples being Database and Services. These sloppy names invite more and more methods that result in the principle of single responsibility being broken.

class UserOperations
  public logIn()
  public annoyUser()
  public punishUser()
  public punishUserAgain()
  public remindUserOfTheirPlaceInTheWorld()
  public takeAwayEverythingUserHoldsDear()
  public updatePassword()
  public threatenLegalActionAgainstUser()
  public deleteUser()

A naming convention called ModelAction helps us name our service classes and their methods, while encouraging single responsibility. A service class named with the ModelAction convention looks like this.

class InvoiceEmailer
  public emailInvoice(Invoice invoice)

In this example, the model is an 'invoice' and the action is 'email'. This class emails an invoice so it is an InvoiceEmailer. The class has one public method with a name that swaps the model and action, in this example, emailInvoice(). This service method's first parameter is of the same type as the model.

public emailInvoice(Invoice invoice)

It can also have additional, required parameters.

public emailInvoice(Invoice invoice, string recipient_email)

This convention encourages the principle of single responsibility. A name like InvoiceEmailer tells you exactly what the class does, and it tells you that that is all it does. Only a maniac would expand an InvoiceEmailer to createInvoiceForSubscription() or copyInvoiceToBackup(). It is easy to see these methods being added to a poorly named class, like InvoiceControl or FinanceAdmin. A class named with the ModelAction convention is protected from these excess responsibilities by its name.

Follow these steps to create your ModelAction class.

  1. Decide which model you are working with, e.g. Subscription.

  2. Decide the action you are performing, e.g. cancelling.

  3. Combine the model and action to form your class name, e.g. SubscriptionCanceller.

  4. Create the name of your single, public method by swapping the model and action names, e.g. cancelSubscription().

  5. The model becomes the first parameter of your single, public method, e.g. public cancelSubscription(Subscription subscription).

  6. Punish the maniacs who shoehorn in additional responsibility by adding methods like restartSubscription() or punishUser(). Obviously, those methods should go in their own classes called SubscriptionRestarter and UserPunisher.

It is important to understand the principle of single responsibility. It is more important to put it into practice. The ModelAction naming convention puts the principle into practice, even when the principle is not understood.