6

In C# we have Extension methods.

Extension methods enable you to add methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

An extension method is a special kind of static method, but they are called as if they were instance methods on the extended type.

However in one of our lesser design moments, we started replacing concrete classes which contain business logic and moving them to Extension Methods.

The advantages seemed neat at first e.g we could replace something like this (very simplified example).

var AddressUtils = new AddressUtils();
AddressUtils.CleanAddress(order);

var ValidationUtils = new ValidationUtils();
ValidationUtils.ValidateCustomer(Order);

with

Order.CleanAddress();
Order.ValidateCustomer();

I.e we were replacing a lot of Stateless instantiable Classes with short-hand static methods (syntactic sugar) that made the code more readable. This has really never sat well with me.

We now have a bunch of static classes with miscellaneous methods in them, the vast majority aren't even generic or interface driven. I.e single use.

Adding to this there is this common notional that static classes are not a good fit for test driven development.

So the alternatives really are

  • Go back to concrete util style helper classes
  • And/Or Encapsulate the logic in some sort of DI service
  • Or add the logic to the Model it self.
  • Or having reusable logic sprinkled out over the various services that need to use them.

In regards to the second last option, order (as most of the the models) are actually Data entities, (and that once again) doesn't seem right (or even feasible) to push the business logic down to the domain models

So i'm left with (unless there are some better patterns) helper classes, Extension methods, DI services, some sort of entity based logic, or a unpredictable spaghetti factory.

2 Answers 2

4

You cited

Extension methods enable you to add methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

and that is when you should use them almost exclusively - where you have a case where it would strongly make sense to extend a type by that method, but you cannot do this, neither by creating a new derived type, nor by recompiling, nor by any other means modifying the original type.

The typical scenario for this is: the type is sealed, and it is either not under your control - maybe a 3rd party library - or you want to avoid adding a method which requires an additional dependency which must not be added to the assembly where the type is defined.

So the first thing to check here is if these constraints apply, and if not, the favored place for putting an additional method should be the class itself. If the the code cannot be changed, but the class is not sealed, one might consider to use subclassing instead of an extension method. In this situation, I would prefer extension methods over subclassing unless access to protected members is needed. There is actually no benefit in the use of subclassing in this situation, neither in testability nor code organisation.

About testability: extension methods are in no way easier or harder to unit test than any other static or non-static method. Testability or TDD becomes only a concern when other code which relies on such a method needs to be tested in isolation. So if you run into a situation where you need to "mock out" the extension method itself, it is time to refactor the method into an utility class which can be replaced more easily by a mock for testing purposes.

So usage of extension methods is fine, if you

  • have a method which fits clearly (!) to the area of responsibility of the type you want to extend

  • cannot modify the type itself easily

  • cannot or do not want to use inheritance

  • don't have the requirement for mocking the function in code which relies on it.

Note there is actually no reason why you cannot start with an extension method, and when you notice you run into its restrictions, refactor the code. Thus, don't overthink this. Note also this has not much to do with the code being business logic or UI code or infrastructure code - when a program is large enough, each of these "layers" can consist of different sublayers and components, which can cause a situation where a "cross-border" type extension is required.

2

Adding to this there is this common notional that static classes are not a good fit for test driven development.

This only holds true if those static methods access or mutate external state. If,

Order.CleanAddress();

only modifies Order and doesn't eg access rules via a static service, then it is not a misfit as far as testing goes.

So if your only reason for considering a change is for testing purposes, and if your methods are well behaved, then leave the code as is.

3
  • The problem with unit testing in context with static methods (or extension methods) is not that one cannot test the static methods themself. The problem is one cannot easily mock them out when testing code which depends on them (which I actually mentioned in my answer).
    – Doc Brown
    Commented Jan 23, 2018 at 10:10
  • 3
    @DocBrown, If a static method doesn't access or mutate global state then it does not need mocking.Mocking is a tool of last resort for code that cannot be easily tested. If something doesn't need mocking, then don't mock it. Therefore "one cannot easily mock them out" is not a sensible argument against well-behaved static methods.
    – David Arno
    Commented Jan 23, 2018 at 10:19
  • If a component X uses a complex function f, and one wants to test X in isolation, then f needs to be mocked for this test. This is completely indepent from f mutating global state or not.
    – Doc Brown
    Commented Jan 23, 2018 at 10:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.