Functional code is easier to test than code with state or side effects. However, most developers spend the majority of their time in traditional, imperative languages. There’s plenty of value in those imperative languages, but these days when I use one, I also try to bring in applicable functional concepts.
In this post I will explore how a stateful, Object-Oriented approach to composition became painful to me, and in my next post I will discuss my proposed functional solution.
Stateless Single-Responsibility Objects & Their Limitations
I’ve written before about an approach I’ve used to compose C# applications, which I call “Stateless Single-Responsibility Objects” (SSROs). In short, most classes in the code are “do-er” objects who know how to perform one task and who are stateless save for their dependencies.
It is fairly easy to write unit tests against the leaf classes (those with no dependencies). As for the node classes, which mostly shuffle data through their dependencies, I use mocking to supply the dependencies and then test their interaction, rather than testing the dependencies along with the class itself. Of course I also have system tests to capture the interplay of all these classes as a whole.
An Example
Let’s see what a typical SSRO solution looks like for a fairly simple problem: solving the pythagorean theorem and then returning the result as a string. Naturally this code is going to be overkill for the problem at hand, but it nicely shows dependency composition and how it becomes frustrating with SSROs.
This is our end goal interface:
public interface IHypotenuseDisplayer
{
public string DisplayHypotenuse(double x, double y);
}
To create an implementation with SSRO, however, we will need each of the following:
public interface IAdder
{
public double Add(double x, double y);
}
public class Adder : IAdder
{
public double Add(double x, double y)
{
return x + y;
}
}
public interface IMultiplier
{
public double Mult(double x, double y);
}
public class Multiplier : IMultiplier
{
public double Mult(double x, double y)
{
return x * y;
}
}
public interface ISqrtCalculator
{
public double Sqrt(double x);
}
public class SqrtCalculator : ISQrtCalculator
{
public double Sqrt(double x, double y)
{
return x * y;
}
}
Ugh. That’s already a lot of boilerplate and repetition. I especially can’t stand the repetition of meaning between class name and method name. You could use the virtual
keyword instead of the interfaces, and this would cut out quite a bit of boilerplate. That said, mocking concrete classes—which you would then need to do in your tests—can be painful (dealing with constructors) and dangerous (partial mocking). So I prefer interfaces, if you are going with this approach.
We still have some more classes to write:
public interface IHypotenuseCalculator
{
public double CalculateHypotenuse(double x, double y);
}
public class HypotenuseCalculator : IHypotenuseCalculator
{
private readonly IAddr _adder;
private readonly IMultiplier _multiplier;
private readonly ISqrtCalculator _sqrtCalculator;
public HypotenuseCalculator(IAdder adder, IMultiplier multiplier, ISqrtCalculator sqrtCalculator)
{
_adder = adder;
_multiplier = multiplier;
_sqrtCalculator = _sqrtCalculator;
}
public double CalculateHypotenuse(double x, double y)
{
return _sqrtCalculator.Sqrt(_adder.Add(_multiplier.Mult(x,x),_multiplier.Mult(y,y)));
}
}
Even more ugh. Using state to hold dependencies is definitely a smell to me. Additionally, future developers could fail to follow my convention and introduce actual state to this class. And it goes without saying that the constructor/field assignment adds a whole boatload of boilerplate.
public interface IDoubleToString
{
public string ConvertToString(double x);
}
public class DoubleToString : IDoubleToString
{
public string ConvertToString(double x)
{
return x.ToString();
}
}
public class HypotenuseDisplayer : IHypotenuseDisplayer
{
private readonly IHypotenuseCalculator _hypotenuseCalculator;
private readonly IDoubleToString _doubleToString;
public HypotenuseDisplayer(IHypotenuseCalculator hypotenuseCalculator, IDoubleToString doubleToString)
{
_hypotenuseCalculator = hypotenuseCalculator;
_doubleToString = doubleToString;
}
public string DisplayHypotenuse(double x, double y)
{
return _doubleToString.ConvertToString(_hypotenuseCalculator.CalculateHypotenuse(x, y));
}
}
So that’s a lot of boiler plate for five functions and encapsulating some dependencies. In addition, if you follow the classname == filename convention, you’ve split up this functionality into at least five files, even if there are no other consumers of HypotenuseDisplayer’s dependencies.
Now what?
There are definitely some shortcomings to this approach. Is there a way to provide the same level of decomposition / adherence to single-responsibility, without so much boilerplate and without the awkward concessions to testability?
Yes.
More on that in my next post.
This is the first post in a series on functional C# application composition:
Hi!
It was an article very interesting! I am excited to see how it continues because I have too the same problems, it seems to me too a code smell to have that dependencies on the constructor.