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?