3

At work we have an interface that is getting bloated. The interface is designed to be easily implemented by an immutable object. So it looks something like this:

//there is no behavior here, just data access
interface DataBlock {
    String name();
    Integer idNumber();
    Double score();
    Instant time();
    //...12 additional methods with arbitrary return types
} 

There are several places in the code where it would be useful to represent the values inside the DataBlock as a Map. For example, when implementing a Builder or Factory.

My initial question was: What should the generic type of the map be?

Keying on interface method names yielded: Map<String, Object> dataBlockAsMap = Data. I didn't like this option because it's easy to mistype the method name. For example, It will fail silently if anyone writes map.get("iDNumber") instead of map.get("idNumber").

I am currently leaning toward Map<DataBlockField, Object> were DataBlockField is an enum with one type for each method in the interface. The upside of this approach is (1) each enum value can "know" its required type, and (2) it makes it easy to write batch operations like:

public static Map<DataBlockField, Object> asMap(DataBlock block) {
    Map<DataBlockField, Object> map = new HashMap<>();
    for(DataBlockField field : DataBlockField.values()) {
         //enum helps extract a value from datablock
         map.put(field, field.getFrom(block));
    }
}

//and

public static String toString(DataBlock block) {
    StringBuilder sb = new StringBuilder();
    for(DataBlockField field : DataBlockField.values()) {
         //enum helps extract a value from datablock
         sb.append(field.getFrom(block)); 
    }
}

My question is: Is introducing this "Interface backed enum" a good idea? I ask because I might be missing something. On the surface this "interface backed enum" is vaguely similar to the Builder Pattern. However, I see Builders left and right. Yet I have never seen an enum written to mirror the methods in an interface.

Am I missing something here?

6
  • Why are you using an interface for this though? All these functions seem to be getter methods, meaning the implementation will be the same in every class that implements this interface. If that's the case, it seems to me you're structuring your code incorrectly in a way that violates the Single Responsibility Principle. Are the classes that would be implementing this interface doing work that could actually be split into two separate classes? I can't see a reason you'd be doing this otherwise.
    – imnota4
    Oct 12, 2016 at 15:19
  • @imnota4 There are two main reasons for the interface. First, these objects need to be serialized. The serialization scheme can place certain requirements on the internals of the object. I don't want those limitations exposed (I can store TypeA in the object itself but return Integer from a gettor method). Second, certain implementations will be much more memory efficient than other (The types returned can be more complex than what is shown here)
    – Ivan
    Oct 12, 2016 at 16:50
  • Have you considered "unbloating" DataBlock first ?
    – Spotted
    Oct 13, 2016 at 7:03
  • @Spotted "unbloating" isn't really an option. A DataBlock contains the data it does. All data can be valuable. It just so happens that many algorithm only use certain subsets of the fields.
    – Ivan
    Oct 13, 2016 at 13:25
  • 1
    @Ivan Why are there not many abstractions in this case ? If some algorithms need a subset of the fields and other algorithms need another subset but you provide all the fields each time, it looks like your design is flawed, or ?
    – Spotted
    Oct 13, 2016 at 13:27

1 Answer 1

1

I think using an enum in this way is a reasonable design.

The only drawback is the not-necessarily-obvious requirement for maintenance of your enum when the interface is maintained/extended.

Alternatives as food for thought:

(1) provide an abstract base class (instead of an interface) that provides both the fields and the serialization. (This may not be practical given an existing code base and the limitation of single inheritance.)

(2) use reflection to enumerate the methods in the interface. This way you can write methods that accomplish the same as the enum though they can pick up any methods in the interface. (You may shudder at using reflection, but you could do this in a more static way than one might think, since all you need to reflect on is the methods of the interface, once, statically and save that, rather than on every given object. You would still be doing the dynamic .invoke(), though.)

(3) if this was C# (I don't know the Java equivalent, but see here) I'd further suggest using (T4) templates or some automatic code generation tooling or a build time program to generate both the interface code and the enum code, together so they stay in sync automatically. In the templating/DSL input, you'd list the fields and their types, and then the tool would generate both the interface as well as the enum that provides the .getFrom() accessors.

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.