Gunnar has I had a long discussion and disagreement on the behavior of Option in Hibernate
OGM that we could not resolve. We would like your feedback.
I did design Option to represent three key concepts:
- an option family - for example named query
- an option per se - for example the combination of the named query family and the query
name
- an option value - for example the named query value (the HQL)
These notions used to be separated in a previous incarnation but for the sake of
simplification, we decided to merge option and option value.
So when I designed Option I added the notion of option identifier that combined with an
option class (the family) would uniquely identify an option. This notion is represented by
getOptionIdentifier() which is used by equals() and hashCode(). The idea is that an Option
instance uniquely identifies an option and that we could detect when a user tries to
define the same option several times (with or without different values).
We have two kinds of options:
- generic options like named query that required an additional key to be uniquely
identified
- unique options like show_sql where the option family and the option are one and the same
thing
In the unique option case, a UniqueOption subclass force the implementation of
getOptionIdentifier so that equals / hashCode are true if the same option / option family
are requested.
A goal of getOptionIdentifier() was to free the Option implementor from thinking too much
about the equals() / hashCode implementation and just focus on the option identity.
Now Gunnar had a concern when he experimented with unique options
class ShowSql extends UniqueOption {
private String value;
public ShowSql(String value) {
this.value = value;
}
public static ShowSql TRUE = new ShowSql("true");
public static ShowSql FALSE = new ShowSql("false");
}
In this situation, ShowSql.equals(ShowSql.FALSE) == true and that bothers him.
So he changed the implementation of Option / UniqueOption to behave like I previously
explained for generic options but to properly return ShowSql.equals(ShowSql.FALSE) ==
false. To do so, he removed getOptionIdentity() and manually ask (Unique)Option
implementors to implement equals. In his example equals for ShowSql would be based on the
value.
So we are at an impasse.
I hate his approach as the equals behavior is inconsistent between generic options and
unique options. It's very confusing for an Option implementor to figure out what
equals should do. And ice on the cake, the implementor has to implement equals / hashCode
instead of delegating that to the superclass: more code, more opportunities to screw it
up.
To me the fact that ShowSql.equals(ShowSql.FALSE) == true is indeed inelegant but AFAIAC
is a degenerate case and shifts the identity from Option to Option + Option Value which is
useless.
Gunnar cannot figure out what I am even saying and to him equals() should behave naturally
period and the hell with consistency.
I see four ways out:
1. we stay how it is today with the weird equals behavior in case of constants and keep
the concept that an Option is not identified by its value.
2. we move to Gunnar's position that an Option is uniquely identified by its family +
value for unique options and keep generic options identity by family + key (and not
value)
3. we move to Gunnar's position but all the way: an Option is uniquely identified by
its family + value both for generic and unique options
4. we fall back to differentiate in the type system option family, option key, and option
value.
So what do you guys all think of this problem?
Emmanuel