Extract Class & the Single Responsibility Principle

agosto 29, 20083 min

Last week we talked about Extract Method and why it is so important to know and love.  This week, we are going to discuss another very important principle in object-oriented design and how Extract Class can help us build and maintain better code.

If you are familiar with Robert Martin’s book Agile Software Development: Principles, Patterns and Practices, you will recall he recast a very powerful concept (the Single-Responsiblity Principle or SRP) know from the “old days”, aka 1979,: a class should have one, and only one, reason to change. Or as I like to add my own spin to it: a class should only do one thing. The reasoning behind the SRP is if your class has two responsibilities, invariably they become coupled (coupled code == bad) and changes in one will impair or inhibit the ability of the class to meet the other responsibilities – and this is only in the case where you have just two responsibilities.  Imagine the confusion when we have three or four responsibilities in a single class!!!

If SRP is so important, then why are talking about Extract Class refactoring?  This is the refactoring you use when you want to apply the SRP and applying the SRP is key to writing good code, i.e. code that is testable, decoupled, maintainable, etc., etc.

How Do I Find It?: You have one class doing the work that should be done by two.

What Do I Do Once I Find It?: Create a new class and move the relevant fields and methods from the old class into the new class.

Any guidelines you care to share?  I am glad that you asked because I do have a few tell tale signs that a new class is waiting to bust out of your old one:

  1. Find your big classes -> any class with more than a 1000 lines is probably doing more than one thing.
  2. Classes that have “too many” methods or fields\properties -> if class has more than 10 to 15 methods or fields\properties, you are probably missing an abstraction.
  3. Methods that don’t belong -> every class has a reason for existence, or a theme, that unifies the method and data.  Methods or data that don’t “fit in” with the theme are likely candidates for a new class.

In an application I am working on, we have a real simple Bus class that shepherds messages between simulated hardware and simulated devices (in our case we are talking about drawers, buttons, LED, etc.).  When you examine the class you see it is about 250 lines, has three fields, a number of events around connecting and disconnecting devices and some private methods.  Looking further, you can find two methods which stick out and don’t match the theme of the class:

45  private IBusMessage BytesToBusMessage(byte[] bytes)
46  {
47      // convert to string
48      string msg = Encoding.ASCII.GetString(bytes, 0, bytes.Length);
49      // split the msg into address and actual command
50      string[] splits = msg.Split(new char[] { '|' });
51      BusMessage message = new BusMessage();
52      message.Address = long.Parse(splits[0]);
53      message.Message = splits[1];
54      if (splits.Length > 2)
55      {
56          message.SequenceNumber = int.Parse(splits[2]);
57      }
58      return message;
59  }
60
61  public static byte[] BusMessageToBytes(IBusMessage message)
62  {
63      // form the message
64      string msg = message.Address + "|" + message.Message + "|" + message.SequenceNumber;
65      return Encoding.ASCII.GetBytes(msg);
66  }
67

So we have a class which is responsible for event registration AND parsing messages.  My question is this: why in the world does a class which mostly deals with event registration have anything to do with parsing?  Why would this class even care how we parse messages back-and-forth?  It sounds like a class doing more than one thing – that is a violation of the SRP – and we will use Extract Class to apply the SRP to Bus.

Next week, we will talk about the Move Method refactoring, which is how you extract your class from the old one.