Kevin Cress
Kevin Cress

Reputation: 268

Am I using interfaces correctly?

My primary reason for using an interface is to make unit testing easy. But I feel like I am not understanding how to use interfaces 100%.

Here's a simplified version of what I'm trying to do:

public interface IPlan
{
    List<Plan> GetPlans(IPlan plan);
    List<Plan> GetAllPlans();
    List<string> DoSomethingElse();
}

public class Plan : IPlan
{
    List<Plan> GetPlans(IPlan plan)
    {
        // 
    List<Plan> planList = plan.GetAllPlans();

    //
    // Do stuff with planList...
    //
}

For setting up a unit test I need to be able to mock the Plan class and return a set list of plans from GetAllPlans, so I see I should be using the interface here so that I can mock it properly. I also understand (or at least I think) that I should be passing in an IPlan object to GetPlans and then I should be using that IPlan object to call GetAllPlans... but something doesn't seem right with this set up.

Am I setting this up incorrectly? It seems weird that I am passing an IPlan that is the same type as the class that it is in (I'd be passing in an IPlan that is a Plan). Any advice on this?

Upvotes: 1

Views: 170

Answers (4)

D Stanley
D Stanley

Reputation: 152624

Well, technically your implementation is correct, but it seems odd for an interface to use a concrete implementation of that interface in its methods. it looks like IPlan is trying to "do too much".

I would instead have something like this:

public interface IPlanRepository
{
    IEnumerable<IPlan> GetPlans(IPlan plan);
    IEnumerable<IPlan> GetAllPlans();
    IEnumerable<string> DoSomethingElse();
}

public class PlanRepository : IPlanRepository
{
    IEnumerable<IPlan> GetPlans(IPlan plan)
    {
        // fill a List with Plans
    }
    IEnumerable<IPlan> planList = GetAllPlans();
    {
        // fill a List with Plans
    }
    //
    // Do stuff with planList...
    //
}

public interface IPlan
{
   // properties and methods relating to a Plan (NOT how to "get" a Plan)
}

public class Plan : IPlan
{
    // implementation of properties and methods
}

Note that I also change the List return types to the more generic IEnumerable interface. This gives you flexibility in the implementation to return anything that returns an IEnumerable<Plan> such as List<Plan>, Plan[], or IQueryable<Plan> if you are using an ORM.

Upvotes: 0

David Arno
David Arno

Reputation: 43264

Your confusion stems, I think, from the off nature of your Plan class. You have a class that presumably encapsulates some plan data, but that also provides methods to handle lists of itself.

Without seeing more code, it's hard to be sure, but you should probably start off by moving GetPlans and GetAllPlans into another class that handles lists of plans. Then it makes sense for their signatures to change to

List<IPlan> GetPlans();
List<IPlan> GetAllPlans();

of possibly even for the class to be generic, constrained on T : IPlan an thus the signatures being:

List<T> GetPlans();
List<T> GetAllPlans();

Upvotes: 2

Obsidian Phoenix
Obsidian Phoenix

Reputation: 4155

An Interface is a contract, designed to be (potentially) applied to many objects. It basically tells your application that any object implementing the interface will implement the methods you are specifying in the interface.

It's designed for loose coupling. Instead of your method accepting a Plan object, e.g:

public bool DoSomething(Plan plan)
{
//....
}

You can make it take IPlan:

public bool DoSomething(IPlan plan)
{
//....
}

This means that at runtime, you could pass anything into that method, so long as it implements IPlan.

This also means that, for unit testing, you can create a dummy implementation of IPlan (for instance, if IPlan normally communicates with the DB, you would not want it to happen in your test) and pass it - so that you are only testing the code in the method, not that method and anything it calls inside Plan.

Upvotes: 0

Ahmed ilyas
Ahmed ilyas

Reputation: 5832

you are implementing it correctly. Every method defined in the interface must be implemented in your concrete class.

what I don't understand in your question is...

"It seems weird that I am passing an IPlan that is the same type as the class that it is in (I'd be passing in an IPlan that is a Plan). "

can you clarify?

if you mean the List GetPlans(), then things like this are totally fine and expected. you are planning to return a collection of plans.

the interface defines some skeleton methods that every class implementing should implement. this basis a "contract" to guarantee that the classes will have this interface implemented. this also means you can do things like plugins where you know you have an IPlugin interface and you can use say reflection to load assemblies which has this interface implemented and start calling methods in the classes.

perhaps you need an entity class called Plan and then rename your current Plan class which implements IPlan to PlanOperations or something. Seems that this class will be doing the work and your Plan is just an entity/object which may have get setters

Upvotes: 0

Related Questions