7

I had a discussion with a co-worker about interface members having parameters that some implementations don't use.

Say I have an interface

interface IDoctor
{
    string GetMedicalOpinion(Age age, Weight weight, SleepSchedule sleepSchedule, Symptom symptom)
}

Implementations of IDoctor won't necessarily use every method parameter:

class BadDoctor : IDoctor
{
    string GetMedicalOpinion(Age age, Weight weight, SleepSchedule sleepSchedule, Symptom symptom)
    {
        if (Symptom == Symptoms.Fatigue)
            return "You're sick!";
        return "You're okay."
    }
}

My co-worker thinks that the interface is poorly defined because there are implementations that don't use every method parameter.

I think this is fine. Having one method with all of the parameters gives us polymorphism -- we can swap in IDoctors of varying competence without modifying the surrounding code. Also, the parameters are immutable data, so consumers shouldn't have any expectation that the function does anything other than return a string. Finally, the parameters are all things that consumers should have naturally, so providing the info isn't a burden on consumers.

Does this violate contemporary design principles? Is there really a problem with interface members having parameters that are used by some implementations, but ignored by others?

3
  • 2
    How would your coworker define the interface, assumed there are other implementations which need all parameters?
    – Doc Brown
    Commented May 14, 2016 at 14:54
  • 1
    Perhaps you can move some parameters to construction arguments if they are only used by specific implementations
    – Ewan
    Commented May 14, 2016 at 15:01
  • @Ewan I don't like that approach. The interface is named IDoctor, not IDoctorService. If I had a class implementing the IDoctor interface taking Symptoms as a constructor parameter, I would assume the symptoms actually belong to the doctor and not the patient the doctor is supposed to inspect. Also what do you do if you want to inspect more patients with the same doctor? It becomes impossible to do.
    – Andy
    Commented May 14, 2016 at 16:41

2 Answers 2

11

I think the main problem is actually caused due to a reason that has been widely discussed recently on this exact board, Should we avoid custom objects as parameters?

In order to call the method you defined, you inherently need to deconstruct the object the attributes belong to.

By changing the method signature to the one in the following example, you eliminate the need to pass unused parameters.

interface IDoctor
{
    string DetermineMedicalState(IPatient patient);
}

It really seems like all the properties you are passing belong to one patient anyway, so keep them together in an object. That's what objects are also used for, to reduce the number of parameters.

With this approach you know the method accepts an IPatient variable and couldn't care less which type of doctor extracts which information from it.


To answer the questions you asked, I don't really think the initial design violates any rules, as long as it respects the contract given by the interface. But it's not an elegant design either and perhaps not even the right one.

By passing an IPatient object you still have polymorphism at hand, thus haven't lost anything.

1
  • 1
    This is one of those cases where passing the Doctor a Patient is the right level of abstraction. This may or may not be true of OP's real code. ++
    – RubberDuck
    Commented May 14, 2016 at 17:26
6

Does this violate contemporary design principles?

No.

Is there really a problem with interface members having parameters that are used by some implementations, but ignored by others?

Not by the fact alone. But if you have 10 different interface implementations, and 9 of them use only parameter 1 and 2, and implementation number 10 is the only one using parameters 3 and 4, that might be an indication that the responsibilities are not cut down in an optimal fashion. If you see such a design, you can ask yourself if the abstraction defined by the interface is fine. Maybe you can move parameter 3 and 4 to the constructor of class 10, as suggested by @Ewan. You should also check if the implementations conform to the Liskov Substition Priciple. But in both cases, you might come to the answer "yes", and let the interface exactly as it is.

This nothing which can be simply decided by counting parameter usage - you have to look at the real code, the function and class names and make a decision based on that information.

1
  • "This is nothing which can be simply decided by counting parameter usage" -- this is the point I was trying to make. I think unused parameters are fine as long as their inclusion isn't surprising and they aren't there to haphazardly thread information through the program. Of course there are bigger design concerns, but there should not be a strict rule against this just on principle.
    – theguy
    Commented May 14, 2016 at 16:35

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.