Where do I put all these interfaces?

Viewed 19350

I'm trying to get my feet wet with unit testing. I'm currently not in the habit of writing interfaces for classes unless I foresee some reason I would need to swap in a different implementation. Well, now I foresee a reason: mocking.

Given that I'm going to be going from just a handful of interfaces to perhaps hundreds, the first thing that popped into my head was, Where should I put all these interfaces? Do I just mix them in with all the concrete implementations or should I put them in a sub-folder. E.g., should controller interfaces go in root/Controllers/Interfaces, root/Controllers, or something else entirely? What do you advise?

6 Answers

Coding to interfaces goes far beyond being able to test code. It creates flexibility in the code allowing a different implementation to be swapped in or out depending on product requirements.

Dependency Injection is another good reason to code to interfaces.

If we have an object called Foo that is used by ten customers and now customer x wants to have Foo work in a different way. If we have coded to an interface (IFoo) we just need to implement IFoo to the new requirements in CustomFoo. As long as we don't change IFoo there is not much needed. Customer x can use the new CustomFoo and other customers can continue to use old Foo and there need be few other code changes to accommodate.

However the point I really wanted to make is that interfaces can help eliminate circular references. If we have an object X that has a dependency on object Y and object Y has a dependency on object X. We have two options 1. with object x and y have to be in the same assembly or 2. we have to find some way of breaking the circular reference. We can do this by sharing interfaces rather than sharing implementations.

/* Monolithic assembly */
public class Foo
{
    IEnumerable <Bar> _bars;
    public void Qux()
    {
       foreach (var bar in _bars)
       {
           bar.Baz();
       }

    }
    /* rest of the implmentation of Foo */
}

public class Bar
{
    Foo _parent;
    public void Baz()
    {
    /* do something here */
    }
    /* rest of the implementation of Bar */
}

If foo and bar have completely different uses and dependencies we probably do not want them in the same assembly especially if that assembly is already large.

To do this we can create an interface on one of the classes, say Foo, and refer to the interface in Bar. Now we can put the interface in a third assembly shared by both Foo and Bar.

/* Shared Foo Assembly */
public interface IFoo
{
    void Qux();
}

/* Shared Bar Assembly (could be the same as the Shared Foo assembly in some cases) */
public interface IBar
{
    void Baz();
}
/* Foo Assembly */
 public class Foo:IFoo
{
    IEnumerable <IBar> _bars;
    public void Qux()
    {
       foreach (var bar in _bars)
       {
           bar.Baz();
       }

    }
    /* rest of the implementation of Foo */
}
/* Bar assembly */
public class Bar:IBar
{
    IFoo _parent;
    /* rest of the implementation of Bar */
    public void Baz()
    {
        /* do something here */
}

I think there is also an argument for maintaining the interfaces separate from their implementations and treating these sightly differently in the release cycle as this allows interoperability between components that were not all compiled against the same sources. If fully coding to interfaces and if interfaces can only be changed for major version increments and not on minor version increments then any component components of the same major version should work with any other component of the same major version regardless of the minor version. This way you can have a library project with a slow release cycle containing just interfaces, enums and exceptions.

I disagree quite a bit with the Accepted answer.

1: While technically correct, you do not NEED an interface because you have the option to mock a concrete implementation, you should make an interface for 2 reasons.

You can extend your code with an interface, concrete implementations require modification, if you do not have an extension, once you get a change request.

1.1: You can make TDD(Test driven development) without any actual implemented code, as long as you only create interfaces to test. This will also force you to consider code design before you make an implementation. Which is an excellent approach to coding.

1.2:

but I would recommend only making an interface if there is a reason to make an interface. I often see interfaces created when a class would work fine and be more appropriate in terms of logic.

There is always a reason to make an interface. Because SOLID's open/close principle says you should aim for extending your code rather than modifying it. And this is true for multiple reasons.

1.2.1: It's easier to write new unit tests this way. You will only need the dependency to the concrete implementation you are testing in your code as a subject. (And before you have a concrete implementation you can use a mock)

1.2.2: When you have a concrete implementation, the reference to that concrete implementation will be propagated throughout the system. With an interface, all references will be done by interface, not concrete implementation. This makes extension possible.

1.2.3 If you follow up with all "leaf" piece of code, to follow the principle, if the method has a return, the method can't have a side effect, if the method doesn't have a return, it can only have 1 side effect, you will also automatically split your code up into the "S" part of SOLID, this makes your unit tests small, and very easy to maintain.

2: Interfaces are technically needed, if you want to write clean code. If you want to follow SOLID, I don't see how you can do it, without interfaces.

You will also need to organize your code efficiently when you break about responsibilities, as the more decoupled your code is, the more interfaces and implementations of interfaces, you will have. Thus you need to have a good project management system in place, so you don't have "hundres of interfaces" lying around randomly.

There are so very good guides in books and youtube, udemy, etc. That will teach you this. (and also some poor ones, basically, they increase in usefulness when you have to pay for them in general). You will have to know enough about the subject matter to identify if a free one is good enough, if you plan to make business decision on it, before you do so, at least.

Related