KnowDotNet

Refactoring - Extract Class

by William Ryan

One of the problem we may get into as programmers is that requirements often change and we don't always have a bunch of time to make sure everything fits.  In most instances, customers don't really care about how well your object model conforms to OOP guidelines, they care about how well your software works, when they can have it and how much it costs.  So many times we start cramming stuff in when we are in a hurry and our classes get a little messy.  

I'm going to demonstrate how to clean up a class using a refactoring known as "Extract Class".  Martin Fowler discusses this technique in
Refactoring:  Improving the Design of Existing Code, which is in my opinion, the finest discussion of refactoring I've come across.

Ok, let's use the example on an Employee class.  At first glimpse we'd all agree that an employee has a FirstName and a LastName, perhaps a middle name, all of which could comprise a FullName.   So a typical implementation would look something like this:

C#

using System;

namespace RefactorSamples
{
  
/// <summary>
  /// Summary description for Employee.
  ///
  public class Employee
   {
      
private string firstName;
      
private string lastName;
      
public Employee()
      {
        
//
        // TODO: Add constructor logic here
        //
     }

      
public Employee(string first, string last)
      {
        
this.FirstName = first;
        
this.LastName = last;
      }
      
public string FirstName
      {
        
get{return firstName;}
        
set{firstName = value;}
      }
      
public string LastName
      {
        
get{return lastName;}
        
set{lastName = value;}
      }
      
public string GetFullName
      {
        
get{return this.FirstName + " " + this.LastName;}
      }
   }
}


Now, we'd invoke this code using something like the following:

private void btnOldWay_Click(object sender, System.EventArgs e)
{
    Employee emp =
new Employee();
    emp.FirstName = "William";
    emp.LastName = "Ryan";
  
string fullName = emp.GetFullName();
}


But let's say that some new law comes out and we need to keep track of a whole bunch of other information about the employee, SSN, DOB, Naturalizaton Status and a whole lot more.   Some of that may relate to the employee directly, some of it not.  Does FirstName apply directly to the employee?  It wouldn't be wrong to say yes, but it might be more correct to say that FirstName and LastName belong to our Name. So if we separated out the Name information into a Name class, and then made a Name property in the Employee Class, we'd keep the two isolated.  This would get rid of a lot of code in the Employee class, in this case the entire contents of the Employee Class.  So first we pull out the Name information and "Extract" it into its own class:

using System;

namespace RefactorSamples
{
  
/// <summary>
  /// Summary description for Name.
  ///
  public class Name
   {

      
private string firstName;
      
private string lastName;
      
public Name()
      {
        
//
        // TODO: Add constructor logic here
        //
     }
      
public Name(string first, string last)
      {
        
this.FirstName = first;
        
this.LastName = last;
      }
      
public string FirstName
      {
        
get{return firstName;}
        
set{firstName = value;}
      }
      
public string LastName
      {
        
get{return lastName;}
        
set{lastName = value;}
      }
      
public string FullName
      {
        
get{return this.FirstName + " " + this.LastName;}
      }
   }
}


Now, we adjust our Employee Class:

using System;

namespace RefactorSamples
{
  
/// <summary>
  /// Summary description for Employee.
  ///
  public class Employee
   {
      
private Name empName;
      
public Employee()
      {
        
//
        // TODO: Add constructor logic here
        //
     }

      
public Employee(string first, string last)
      {
        
      }
      
public Name EmployeeName
      {
        
get{return empName;}
        
set{empName = value;}
      }
  
   }
}


Now to use the class we only need to make a small adjustment:

private void btnNewWay_Click(object sender, System.EventArgs e)
{
    Employee emp =
new Employee();
    emp.EmployeeName.FirstName = "William";
    emp.EmployeeName.LastName = "Ryan";
  
string fullName = emp.EmployeeName.FullName;
}


Now, although it wasn't incorrect to say an Employee has a LastName for instance, it probably makes more sense to say an Employee has a Name and that name is comprised of a FirstName, a LastName, perhaps a middle name etc.  Why clutter up the employee class when you can have such a natural extraction?  It saves space in the class and as such, everything you may subclass from it.  It may also allow for greater reuse.  What if we had a StockHolder class. We could easily incorporate the name class into it.  However, if we stuck the name data into the Employee class the only way to do this would be to create an employee for each stockholder.  Logically this is awful and would be confusing to anyone trying to understand the object model.  The other alternative would be to create Name information for the StockHolder class. That would cause us to rewrite the same code over and over.  If OOP is about code reuse, then this certainly isn't living up to the ideal. If you reuse code you've written you have an absolutely ugly implementation.  The other alternative is to reinvent the wheel.  Copy and paste makes it somewhat easier, but it's still a waste.  And every modification will cause you to have to remember everywhere you've implemented Name information. The first approach is bad in every regard.

So look through your objects, there's a good chance you can use Extract Class to cut out some redundant code and save yourself a  bunch of headaches.