3

I always followed the opinion to not abuse interfaces in case of decomposition. Usually I only implement them if I am absolutely sure to have a "is-a"-relation and avoid implementing them if there is a "has-a"-relation to another kind of object.

Now, this ethic leads me to a problem: I'm writing a small library including a type containing a "name" property. The objects representing this type are almost exclusively created through a XML-file where they are defined. The idea behind giving those objects a name is to easily access them in the code.

The definition looks like this:

<root>
    <object name="foo">
    ....
    </object>
</root>

In the code it should be possible to apply any representor of this type by name or by the object itself. So the type includes a name:

public interface NamedObject {

    String getName();

    .....
}

So now the following two samples should create the same result:

Sample 1:

NamedObject obj = Utils.getFromXML("obj_name");
Foo.doStuff(obj);

Sample 2:

Foo.doStuff("obj_name");

The reason why this should work is that there may be a context where the programmer is already working with such a named object and so he should be able to use it directly like in Sample 1. But there may be a context where he wants to call a method depending on such an object and the programmer might want to use a named object he defined in the XML. Then he could just lookup the name and use it via its name.

The actual problem is, that this is leading me to a mess in my structural design as there are a lot of objects requiring those kind of objects in their methods and in most cases there is a default behavoir defined as well.

So if there is a default behavior I need 3 implementations of the method.

public interface Bar {

    void doStuff()

    void doStuff(String namedObjectName);

    void doStuff(NamedObject object);
}

So 6 functionalities for example would lead to 18 methods.

I think there might be no other way to represent the default behavoir than providing a method without parameter. I could run the default behavoir by accepting null pointers and run the default behavoir if a null pointer is given but in my eyes, a param-less method is the prettier solution.

So to get rid of one of the other two signatures the NamedObject-type could inherit from java.lang.CharSequence. The implementations of the methods of java.lang.CharSequence could be done by using the name property. Since Java 8 I could already default implement them in the interface using the getName() method.

With this move I could replace the method accepting the java.lang.String parameter representing the name and the method accepting the NamedObject with a single method accepting a java.lang.CharSequence representing either the name or the object itself.

The NamedObject-type itself CLEARLY DOES NOT represent a "is-a" relation to its name. It represents a "has-a" relation.

And that's the question. What's the best way to design a library including such a structure? Is my new intention ok?

4

Inheriting from java.lang.CharSequence will probably cause you much more maintenance headaches in the future, not just because of the has-a/is-a design glitch.

I think the real problem is that you are trying to support calls like this directly:

  Foo.doStuff("obj_name");

That way, your Foo class needs to be coupled to Utils.getFromXML, which makes it probably more complex than it needs to be. Lets ignore the "default behaviour" part of your question for a moment. So I recommend to design your interface just like this:

public interface Bar {
    void doStuff(NamedObject object);
}

and if you need this, provide a helper or utility class XmlUtil containing a function like this

public void DoStuff(Bar bar, String objname)
{
    NamedObject obj = Utils.getFromXML(objname);
    bar.DoStuff(obj);
}

So you can call it like

XmlUtil.DoStuff(Foo, "obj-name");

which is only a little bit longer than your original call, but makes the Xml context explicit.

That way, you do not have to provide the same "string -> object" mapping in each Bar implementation over and over again, you can reuse the function above for any Bar implementation. Moreover, only your utility class now needs to be coupled to the Xml directly, not your `Bar implementations.

  • Thank you! I think i will either do it your way or by default imlpementing the extra methods in interface as Java 8 provides this – narranoid Sep 7 '15 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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