I have been going back and forth in a discussion about polymorphic enums to call different DAO methods depending on enum entry, and I haven't been able to get a common agreement on this subject.
Lets say you have an enum that controls your response style, (short and detailed), and then you override a method to get that info at enum level, something like what I wrote below
public enum ResponseStyle {
short {
@Override
public List<Response> get(){
calls to some database template method
},
detailed {
@Override
public List<Response> get(){
calls to some database template method
}
}
}
And that code is called from DAO with responseStyle.get
, is that bad design? My intent was to future proof possible new response style implementations, and go towards heuristic determination instead of chaining If or Switch statements, but I wonder if this breaks the DAO pattern.
-
OK, so I did a bit of reading about this, and I'm decidedly not a fan. You can do better (and simpler, and easier to understand, and cleaner) by simply using ordinary higher-order functions in a dictionary/hashtable, or factory methods or constructor overloads. See also here.Robert Harvey– Robert Harvey2019年05月21日 14:47:04 +00:00Commented May 21, 2019 at 14:47
-
@RobertHarvey when u saying higher-order functions, can u give an example? U have different templates for each enum entry, same dao, how to handle it in a clean manner ?Danilo Silva– Danilo Silva2019年05月21日 18:51:07 +00:00Commented May 21, 2019 at 18:51
-
I think you are talking about returning a method reference to a DAO method from this get in Enum, but not sure if thats it.Danilo Silva– Danilo Silva2019年05月21日 18:56:38 +00:00Commented May 21, 2019 at 18:56
-
This example uses Javascript, but it illustrates the basic idea: hackernoon.com/…Robert Harvey– Robert Harvey2019年05月21日 19:01:14 +00:00Commented May 21, 2019 at 19:01
3 Answers 3
You should ask yourself if the behavior of the program changes between short and detailed ResponseStyle
or if purely the data changes. If it is the latter, then enum should, if anything, should use its label as a prefix in a lookup in a properties file with all the data required. The behavior therefore remains the same and consistent.
If the behavior changes, then you're potentially looking at what would probably be a very complex enum declaration with multiple behaviors, each one inconsistent with another. In that case you should probably prefer a factory method or even an abstract factory pattern, which when given a ResponseStyle
enum, will appropriately give a list of Response
instances.
In either case, I feel that your approach is not ideal. Enums can be the pivot which connects usage and implementation, but avoid cluttering whenever possible.
-
As of now, the enum only controls whether a projection list is passed to mongo or not, and different methods are called. The data returned looks different, but the problem is that different methods are supporting each style. Also I am not a fan of having a huge chain of if statements to evaluate everything. From my point of view, the change in behavior is just a different method call, and going to a AF pattern seems like over-engineering, plus YAGNI, since nothing else changes between the two styles.Danilo Silva– Danilo Silva2019年05月21日 10:32:15 +00:00Commented May 21, 2019 at 10:32
-
@DaniloSilva You can still accomplilsh this using enums, just don't make it the focal point. If anything, associate the enum with a class and then call that class. At least it keeps the enum simple.Neil– Neil2019年05月21日 10:48:09 +00:00Commented May 21, 2019 at 10:48
-
I reverted back to if evaluation in DAO level to call a different method. The enum will never grow anyway.Danilo Silva– Danilo Silva2019年05月21日 12:39:06 +00:00Commented May 21, 2019 at 12:39
The entire point of DAO is that you use a gateway (the DAO object) to transform your programming representation into your database representation, and your database representation into your programming representation. This permits you the flexibility to use Enums in the first place, so using Enums is not a bad thing.
With this in mind, tightly binding Enums can easily become a bad thing because updates to the Enum trigger the need to recompile all modules, even if the modules fail to use the newly added Enum fields. There's a pattern that avoids this, the "Extensible Enum Pattern".
Basically, you create an Interface. Then you create a Enum that implements the Interface. Then you rework you code to accept the Interface, and pass the Enum into the methods as before.
Doing this generally requires your Enum to be slighly renamed, as it is now extensible. For example, an SwitchPosition
Enum might contain ON
and OFF
as entries, but under this pattern, it might be better to call the interface SwitchPosition
and the Enum DefaultSwitchPosition
. Doing so might permit someone to pass in a three position switch position of MIDDLE
, provided they extend the Interface, even if that element isn't in the DefaultSwitchPosition
Enum.
Such extensibility may be all that is needed to settle the concerns of the person around this DAO interface.
I have a flaw in following the question's logic.
What I understand is an enum is a collection of singleton instances, a DAO enum is a collection of DAO instances, for the actual case ResponseStyle
DAO instances, that is the implementation of strategy design pattern. What I fail to understand is what is bad and that makes it difficult to support with arguments whether it is or not bad. Being an implementation of several design patterns I hazard and say it is a possible recommended implementation.
Following is just a preference useful when the enum instances have to be retrieved using string identifiers, otherwise is useless.
The implementation can be improved since the valueOf
method of an enum is case sensitive and its argument has to be the name of one of enum's values it is tightly coupling valueOf
method calling code with the string version of the enum's values names. The improvement would be to extend the enum with a string field with the sole purpose to identify enum instances and a static factory method to return an enum instance for a given string. Also the enum instances Java naming convention is using capital letters. Following is an implementation suggestion that returns the first enum value when it can't find a matching between the given string identifier and the string identifier of one of the enum's values.
public enum ResponseStyle {
SHORT("short") {
@Override
public List<Response> get() {
// calls to some database template method
}
},
DETAILED("detailed") {
@Override
public List<Response> get() {
// calls to some database template method
}
}
private String code;
private ResponseStyle(String code) { this.code = code; }
public List<Response> get() { return Collections.EMPTY_LIST; }
public static ResponseStyle of(String code) {
ResponseStyle responseStyle = ResponseStyle.SHORT;
for ( ResponseStyle value : ResponseStyle.values() ) {
if ( value.code.enqualsIgnoreCase(code) ) {
responseStyle = value;
break;
}
}
return responseStyle;
}
}