5

I'm designing an architecture in which I have:

public interface IObjectReader{
    public Object read();
}

public class ConcreteObjectReader implements IObjectReader{
     @Override
     public Object read(){
         //do stuff
         return new Object();
     }
}

Now, if I want to allow a reader to be parameterized, I could create another interface this way:

public interface IParameterizedObjectReader extends IObjectReader {
    public void setParams(Map<String,String> params);
    public Map<String,String> getParams();
}

To me this is reasonable. However, in my project I have also object writers, object processors and so on, and they also need to be, eventually, parameterized. In this scenario, having two or more interfaces that define the same contract (getParams and setParams) is a very bad idea to me. So I would define an interface IParameter like this:

public interface IParameter{
    public void setParams(Map<String,String> params);
    public Map<String, String> getParams();
}

and I'd make the IParametereziedObjectReader interface (similar for IParameterizedObjectWriter, etc) extend IParameter too, but I'd leave it empty.

Is this a bad idea? Or maybe I should leave only the IParameter interface and delete its subinterfaces? For clarity I have to say that I don't use these Parameterized interfaces as markers anywhere in my code, so they'd be empty just for an architectural reason.

How would you design this solution differently?

  • what language is this, c#? – gnat Jul 15 '17 at 8:40
  • 5
    @gnat, looks like Java to me, eg the @Override. – David Arno Jul 15 '17 at 8:44
  • 2
    I think It adds unecessary complexity. I don't see the point in extending interfaces if we are not enhancing extended interface. It makes the design convoluted. Frameworks like Spring Data lead us to similar practices and I have find them to be tricky and hard to understand from the design point of view. Remember that inheritance was not made for saving LOC. – Laiv Jul 15 '17 at 10:32
  • 2
    @Ewan How do you express "this variable/parameter has to implement both IObjectReader and IParameter"? – svick Jul 15 '17 at 11:06
  • 1
    Which one of your examples is an empty interface? It seems to me that all of them have at least a method signature. – Tulains Córdova Jul 16 '17 at 0:46
13

Are empty interfaces (but not marker interfaces) a bad programming practice?

Generally, yes. By definition, empty interfaces provide you nothing. They can be marker interfaces (generally evil), or aliases for another type (occasionally useful due to legacy code when renaming something). But in a greenfield project like this they're just over-engineering and YAGNI.

How would you design this solution differently?

Without knowing more about your requirements, I can't say. But trying to make arbitrary read/write/process, with arbitrary number/shape of parameters is a fool's errand. Systems that can "do anything" aren't providing any useful abstraction over just writing code, and tend to be nightmares to implement and maintain. If you want a scripting language, go use a scripting language.

  • 8
    "Systems that can "do anything" aren't providing any useful abstraction over just writing code, and tend to be nightmares to implement and maintain." Preach it!! – whatsisname Jul 16 '17 at 1:40
  • "If you want a scripting language, go use a scripting language." LOL! – Jalal Aug 29 '18 at 5:35
2

Building a tree of interfaces, having one interface inherit from another, will soon defeat the purpose of having interfaces in the first place. You might as well build a class tree and use no interfaces at all.

It is always better to have multiple small interfaces, each with a single feature, than to have one compound interface that does it all but serves a single scenario. With the latter you really tie the interface to a class you already have in the back of your head, making it useless for anything else, hence pointless to have.

So in your particular example you want an IReader and an IParameterizable interface that are independent of eachother, each defining a single feature rather than the full feature set of some arbitrary class.

2

This looks like a job for the Interface Segregation Principle!

You say you want to create this:

public interface IParameterizedObjectReader extends IObjectReader {
    public void setParams(Map<String,String> params);
    public Map<String,String> getParams();
}

Or this:

public interface IParameterizedObjectReader extends IObjectReader, IParameter { }

I ask, do you need this:

IParameter p;

and this:

IObjectReader oReader;

and this:

IParameterizedObjectReader por;

Because if you don't have clients that need them all, you're making things a bit difficult for a hard to see reason. The fact that an interface might be empty because it gets what it promises from the interfaces it extends really means nothing to me. I simply like the lack of duplication. But it doesn't justify or undermine the existence of the interface.

What justifies the existence of an interface is that some client wants to use it. Clients OWN the interfaces. They need the things the interface promises. Nothing cares if it was defined with an empty body. Hell, the interface might simply exist to provide name indirection.

And yes, you can create complexity this way. Don't create it because of "architectural reasons". Create it because something needs it. Now. Today.

Clients shouldn't have to know about methods they don't care about. Follow this rule an your interfaces will be role interfaces. Each will exist for a good reason. Will have a single responsibility. And not result in a combinatorial explosion of an interface for every possible set of methods.

Oh, and remember, the IMyInterface prefix is a C# thing. Java does the MyImplementationImpl suffix thing. Both are terrible things but those are the things.

  • I miss something tho. Stacking interfaces also lead to stack responsabilities. While a IReader consumer should not be aware of the IParametrizable thing, with IParametrizableObjectReader consumer is going to be aware of both what not just breaks ISP, It may result in a SRP violation too. In other words, the IParametrizableObjectReader consumer may end up needing to know how to parametrize the reader and how to execute it. If we end up casting the instance to one or another to avoid ISP violations, we realise that the last one (IParametrizableObjectReader) is just pointless. – Laiv Jul 15 '17 at 15:37
  • @Laiv I wouldn't consider it useless if it lets me avoid casting. If my client needs what IParametrizableObjectReader provides I see no reason to force a client to work with the same reference through two different interfaces. I'd rather create IParametrizableObjectReader. What I don't want to see is every possible combination of interfaces given an interface because it MIGHT be needed someday. Because, OW! – candied_orange Jul 15 '17 at 15:46
0

The fact that reader, writer and processors have methods with common signature is just accidental circumstances.

Introducing IParameter interface will bound your reader, writer and processor abstractions under same contract signature.
After first change request where you need add one more argument to the reader class or change parameter type - you will throw your IParameter away.

Since your application need both parameterless and parameterized readers - consider introducing reader with two methods.

public interface IObjectReader
{
    public Object read();
    public Object read(Map<String,String> params);
}

protected by gnat May 11 '18 at 14:02

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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