Friday, August 07, 2015

C#: Program Entirely With Static Methods

OK, that’s a provocative title to get your attention. This post is really about how one can move to a more functional programming style and remove the need for much of the apparatus of object-oriented programming, including interfaces and classes. In this post, I’m going to take some typical object-oriented C# code and refactor it in a more functional style. I’ll show that the result is more concise and easier to test.

Over the past couple of years I’ve noticed that my C# coding style has changed drastically under the influence of functional programming. Gone are interfaces and instance classes to be replaced by static methods, higher-order functions and closures. It’s somewhat ironic since I spent many years as a cheerleader for object-oriented programming and I considered static methods a code smell.

I guess if I look at my programming career, it has the following progression:

Procedural –> Object-Oriented –> Functional

The OO phase now looks like something of a detour.

C# has all the essential features you need for functional programming – higher-order functions, closures, lambda expressions – that allow you to entirely ditch the OO programming model. This results in more concise, readable and maintainable code. It also has a huge impact on unit testing, allowing one to do away with complex mocking frameworks, and write far simpler tests.

Introducing our object oriented example

Let’s look at an example. First I’ll introduce a highly simplified OO example, a simple service that grabs some customer records from a data-store, creates some reports and then emails them. Then I’ll show the same code refactored in a more functional style using delegates and higher-order static methods.

Let’s look at the object-oriented example first:

Well written object-oriented code is compositional. Concrete classes depend on abstractions (interfaces). These interfaces are consumed as dependencies by classes that rely on them and are usually injected as constructor arguments. This is called Dependency Injection. It’s good practice to compose object instances in a single place in the application - the composition root - usually when the application starts up, or on a significant event, such as an HTTP request. The composition can be hand coded or handed off to an IoC container. The constructed graph is then executed by invoking a method on the root object. This often occurs via an application framework (such as MVC or WebApi) rather than being explicitly invoked by user code.

We are going to get some customer records, create some reports and then email them to our customers. So first we need three interfaces: a data access abstraction, a report building abstraction, and an emailing abstraction:

public interface ICustomerData
{
IEnumerable<Customer> GetCustomersForCustomerReport();
}
public interface IReportBuilder
{
Report CreateCustomerReport(Customer customer);
}
public interface IEmailer
{
void Send(string toAddress, string body);
}
view raw Interfaces.csx hosted with ❤ by GitHub

And here are the implementations. This is not a real program of course, I’ve just coded some dummy customers and the emailer simply writes to the console.

public class CustomerData : ICustomerData
{
public IEnumerable<Customer> GetCustomersForCustomerReport()
{
// pretend to do data access
yield return new Customer("mike@mikelair.com");
yield return new Customer("leo@leofort.com");
yield return new Customer("yuna@yunacastle.com");
}
}
public class ReportBuilder : IReportBuilder
{
public Report CreateCustomerReport(Customer customer)
{
return new Report(customer.Email, $"This is the report for {customer.Email}!");
}
}
public class Emailer : IEmailer
{
public void Send(string toAddress, string body)
{
// pretend to send an email here
Console.Out.WriteLine("Sent Email to: {0}, Body: '{1}'", toAddress, body);
}
}

Now we have our service class that depends on the three abstractions and orchestrates the reporting process:

public class ReportingService
{
public ReportingService(ICustomerData customerData, IReportBuilder reportBuilder, IEmailer emailer)
{
CustomerData = customerData;
ReportBuilder = reportBuilder;
Emailer = emailer;
}
public ICustomerData CustomerData { get; private set; }
public IReportBuilder ReportBuilder { get; private set; }
public IEmailer Emailer { get; private set; }
public void RunCustomerReportBatch()
{
var customers = CustomerData.GetCustomersForCustomerReport();
foreach (var customer in customers)
{
var report = ReportBuilder.CreateCustomerReport(customer);
Emailer.Send(report.ToAddress, report.Body);
}
}
}

As you can see, we inject the dependencies as constructor arguments, store them in class properties, then invoke methods on them in the code in the RunCustomerReportBatch method. Some people like to store the dependencies in class fields instead. That’s a matter of choice.

Our composition root composes the ReportingService with its dependencies and then returns it for the program to invoke. Don’t forget this is a highly simplified example. Composition is usually far more complex:

public static ReportingService Compose()
{
return new ReportingService(
new CustomerData(),
new ReportBuilder(),
new Emailer()
);
}

To write a unit test for the reporting service we would typically use either hand-crafted mocks, or some kind of mocking framework. Here’s an example unit test using XUnit and Moq:

[Fact]
public void RunCustomerReportBatchShouldSendReports()
{
// Arrange
var customerDataMock = new Mock<ICustomerData>();
var reportBuilderMock = new Mock<IReportBuilder>();
var emailerMock = new Mock<IEmailer>();
var expectedCustomer = new Customer("fist@sea.com");
var expectedReportBody = "the report body";
customerDataMock.Setup(x => x.GetCustomersForCustomerReport())
.Returns(new[] { expectedCustomer });
reportBuilderMock.Setup(x => x.CreateCustomerReport(expectedCustomer))
.Returns(new Report(expectedCustomer.Email, expectedReportBody));
var sut = new ReportingService(
customerDataMock.Object,
reportBuilderMock.Object,
emailerMock.Object);
// Act
sut.RunCustomerReportBatch();
// Assert
emailerMock.Verify(x => x.Send(expectedCustomer.Email, expectedReportBody));
}

We first create mocks for ReportingService’s dependencies with the relevant methods stubbed, which we inject as constructor arguments. We then invoke ReportingService and verify that the emailer was invoked as expected.

So that’s our object-oriented example. It’s typical of much well constructed C# code that you will find in the wild. It’s the way I’ve been building software for many years now with much success.

However, this object-oriented code is verbose. About a third of it is simply OO stuff that we have to write repeatedly and mechanically rather than code that is actually solving our problem. This boilerplate includes: the class’ properties (or fields) to hold the dependencies; the assigning of constructor arguments to those properties; writing the class and constructor. We also need complex mocking frameworks simply to test this code. Surely that’s a smell that’s telling us something is wrong?

Enlightenment

Enlightenment begins when you realise that the dependencies and method arguments can actually just be seen as arguments that are applied at different times in the application’s lifecycle. Consider a class with a single method and a single dependency:

public class Thing : IThing
{
public Thing(IDependency dependency) { }
public void Do(string arg) { }
}

We could equally represent this as a static method with two arguments:

public static void DoThing(IDependency dependency, string arg) { }
view raw DoThing.csx hosted with ❤ by GitHub

But how do we partially apply these arguments? How do we give ‘DoThing’ the IDependency argument at composition time and the ‘string arg’ at the point where it is required by the application logic? Simple: We use a closure. Anything taking a dependency on ‘DoThing’ will ask for an Action<string>, because that is the signature of the ‘Do’ method in our ‘Thing’ class. So in our composition root, we ‘close over’ our previously created IDependency instance in a lambda expression with the signature, Action<string>, that invokes our DoThing static method. Like this:

var dependency = new ThingThatImplementsIDependency();
// RelyOnThing is something that that takes Action<string> as a dependency.
Action relyOnThing = () => RelyOnThing(arg => DoThing(dependency, arg));

So the interface is replaced with the built-in Action<T> delegate, and the closure is effectively doing the job of our ‘Thing’ class, the interface’s implementation, but with far fewer lines of code.

Refactoring to functional

OK. Let’s go back to our example and change it to use this new insight. We don’t need the interface definitions. They are replaced by built in delegate types:

ICustomerData becomes Func<IEnumerable<Customer>>

IEmailer becomes Action<string, string>

IReportBuilder becomes Func<Customer, Report>

The classes are replaced with static methods:

public static IEnumerable<Customer> GetCustomersForCustomerReport()
{
// pretend to do data access
yield return new Customer("mike@mikelair.com");
yield return new Customer("leo@leofort.com");
yield return new Customer("yuna@yunacastle.com");
}
public static Report CreateCustomerReport(Customer customer)
{
return new Report(customer.Email, $"This is the report for {customer.Email}!");
}
public static void SendEmail(string toAddress, string body)
{
// pretend to send an email here
Console.Out.WriteLine("Sent Email to: {0}, Body: '{1}'", toAddress, body);
}

Our ReportingService is also replaced with a single static method that takes its dependencies as delegate arguments:

public static void RunCustomerReportBatch(
Func<IEnumerable<Customer>> getCustomersForCustomerReport,
Func<Customer, Report> createCustomerReport,
Action<string, string> sendEmail)
{
var customers = getCustomersForCustomerReport();
foreach (var customer in customers)
{
var report = createCustomerReport(customer);
sendEmail(report.ToAddress, report.Body);
}
}

Composition looks like this:

public static Action Compose()
{
return () => RunCustomerReportBatch(
GetCustomersForCustomerReport,
CreateCustomerReport,
SendEmail);
}

This is functionally equivalent to the object-oriented code above, but it has 57 lines of code as opposed to 95; exactly 60% of the original code.

There’s also a marked simplification of the unit test:

[Fact]
public void FunctionalTest()
{
// arrange
var expectedCustomer = new Customer("fist@sea.com");
var expectedReportBody = "the report body";
Func<IEnumerable<Customer>> getCustomersForCustomerReport =
() => new[] {expectedCustomer};
Func<Customer, Report> createCustomerReport =
customer => new Report(expectedCustomer.Email, expectedReportBody);
var actualToAddress = "";
var actualBody = "";
Action<string, string> sendEmail = (toAddress, body) =>
{
actualToAddress = toAddress;
actualBody = body;
};
// act
Functional.RunCustomerReportBatch(getCustomersForCustomerReport, createCustomerReport, sendEmail);
// assert
Assert.Equal(expectedCustomer.Email, actualToAddress);
Assert.Equal(expectedReportBody, actualBody);
}

The requirement for a complex mocking framework vanishes. Instead we merely have to set up simple lambda expressions for our stubs. Expectations can be validated with closed over local variables. It’s much easier to read and maintain.

Moving to a functional style of programming is certainly a huge departure from most C# code that you find in the wild and can initially look a little odd to the uninitiated. But it has many benefits, making your code more concise and easier to test and reason about. C# is, surprisingly, a perfectly adequate functional programming language, so don’t despair if for practical reasons you can’t use F#.

The complete code example for this post is on GitHub here: https://github.com/mikehadlow/FunctionalDemo