Refactoring: extract interface

Extract interface is one of the most common refactoring techniques. Motivation behindextract interface refactoring method is to avoid direct dependencies between classes. Instead of using classes in method calls we use interfaces so we can also use subclasses of those classes we used before. Also we can create brand new classes that use follow these interfaces and we can use these classes instead of current ones.

Let’s say you have a class that takes some parameters with its constructor. And let’s suppose you want to make this class easily testable.


public class PaymentImporter
{
   
private readonly LogProvider
_logProvider;

   
public PaymentImporter(LogProvider
logProvider)
    {
        _logProvider = logProvider;
    }

   
public void
Import()
    {
        _logProvider.WriteMessage(
"Starting payments import"
);

       
try
        {
           
// Perform importing tasks here
        }
       
catch (Exception
ex)
        {
            _logProvider.WriteException(ex);
        }

        _logProvider.WriteMessage(
"Payments import finished");
    }
}

You cannot test this class easily because you need to provide it with log provider. And you have to use real log provider. But you need to unit test your class. Of course, you can write test that uses current log provider but it is not unit test anymore. It is mix of unit test and integration test and therefore results of this test are pointless. Why? Because this test fails if you have logic errors in your class and also if log provider produces exceptions. You want to remove this dependency and use fake log provider for unit tests.

Extract interface

First step is to create interface for log provider. To be sure that nothing gets broken we create interface using exactly the same public members as original log provider has. If original log provider is something like this


public class LogProvider
{
   
public void WriteMessage(string
message)
    {
       
// Write message to log
    }

   
public void WriteException(Exception
exception)
    {
       
// Write exception to log
    }
}

then our interface will look like this


public interface ILogProvider
{
   
void WriteMessage(string
message);
   
void WriteException(Exception ex);
}

Of course, we have to make original LogProvider class also to follow this interface. Otherwise we are not able to use it in class constructor.

Break dependency

Second step is to break dependency in class constructor. We replace original log provider class name with interface name so our class can accept also other log providers that follow the same interface.


public class PaymentImporter
{
   
private readonly ILogProvider
_logProvider;

   
public PaymentImporter(ILogProvider
logProvider)
    {
        _logProvider = logProvider;
    }

   
// ...
}

Now we have broken dependency between our class and logger class and we can use arbitrary number of log provider implementations instead of original one as long as they follow ILogProvider interface.

Create fake log provider

Now we can create fake log provider using ILogProvider interface. We need fake log provider only for testing purposes and therefore it has to do nothing. If there are properties or methods that return values we make sure that these properties and methods return “correct” values. This way we can be sure that log provider is not able to garble our test results.

Our fake log provider is something like this.


public class FakeLogProvider : ILogProvider
{
   
public void WriteMessage(string
message) { }
   
public void WriteException(Exception exception) { }
}

Now we are able to write tests for our class because we have no direct dependencies with other classes that may produce their own errors and make our test results messy this way.


Leave a Reply

Your email address will not be published. Required fields are marked *