I have several enums which comply to a common interface:
interface TableColumns
{
String getColumnName();
int getColumnIndex();
ColumnType getColumnType();
boolean isEditable();
int getColumnWidth();
}
A typical implementation is:
enum PointsTableColumns implements TrendTableColumns
{
POINTS_COLUMN("points_column", false, ColumnType.TEXT, 400, 0);
private String columnName;
private boolean editable;
private ColumnType columnType;
private int columnWidth;
private int columnIndex;
private PointsTableColumns (String columnName, boolean editable, ColumnType columnType,
int columnWidth,
int columnIndex)
{
this.columnName = columnName;
this.editable = editable;
this.columnType = columnType;
this.columnWidth = columnWidth;
this.columnIndex = columnIndex;
}
public boolean isEditable()
{
return editable;
}
public int getColumnIndex()
{
return columnIndex;
}
public String getColumnName()
{
return columnName;
}
public int getColumnWidth()
{
return columnWidth;
}
public ColumnType getcolumnType()
{
return columnType;
}
}
I have several implementaions like this (10+), each having multiple and almost different values. Now, the problem is that I see a lot of code duplication here, as the methods in all of the implementation are identical word by word. I know that in Java this is virtually impossible as enums can't extend an implementation. What I need here is a suggestion or a different strategy where this can be done in a cleaner way. Is there already some existing pattern regarding this?
If you can live with one level of indirection, then the approach I present below will reduce repeated code to a minimum.
First, consider the following supplier interface, along with its inner class:
public interface PropertiesSupplier {
Properties properties();
public static final class Properties {
private final int value1;
private final String value2;
private final double value3;
private Properties(int value1, String value2, double value3) {
this.value1 = value1;
this.value2 = value2;
this.value3 = value3;
}
public static Properties of(int value1, String value2, double value3) {
return new Properties(value1, value2, value3);
}
public int getValue1() {
return this.value1;
}
public String getValue2() {
return this.value2;
}
public double getValue3() {
return this.value3;
}
@Override
public String toString() {
return "Properties [value1=" + this.value1 + ", value2=" + this.value2 + ", value3=" + this.value3
+ "]";
}
}
}
Nothing magic here. The inner class is just a bean with private final
fields, a private constructor to initialize them, public getters, a factory method and an overriden toString()
method. The interface defines only one method that returns an instance of the inner class. Note that the inner class is final. The idea is to enforce immutability, so that its properties are not allowed to change.
Then, let's create a couple of enums that will implement this interface. Let's start by MyEnum1
, which defines two values:
public enum MyEnum1 implements PropertiesSupplier {
ENUM_1_CONST_1(Properties.of(1, "hello", 0.123)),
ENUM_1_CONST_2(Properties.of(2, "goodbye", 7.54));
private final Properties p;
private MyEnum1(Properties p) {
this.p = p;
}
@Override
public Properties properties() {
return this.p;
}
}
Next comes MyEnum2
, which defines only one value:
public enum MyEnum2 implements PropertiesSupplier {
ENUM_2_CONST_1(Properties.of(9, "hey dude", 547.21578));
private final Properties p;
private MyEnum2(Properties p) {
this.p = p;
}
@Override
public Properties properties() {
return this.p;
}
}
As you see, both enums implement the PropertiesSupplier
interface, so they must provide an implementation for the Properties properties()
method. And in order to comply with this, they must encapsulate an instance of Properties
, which they receive in their constructor.
So now, after this indirection, the only code that is repeated among all enums is just the Properties
field, the constructor that receives it as an argument and its getter method.
This is a sample showing how the enums would be used:
MyEnum1 e1 = MyEnum1.ENUM_1_CONST_2;
MyEnum2 e2 = MyEnum2.ENUM_2_CONST_1;
System.out.println(e1.name() + " - " + e1.properties());
System.out.println(e2.name() + " - " + e2.properties());
This code produces the following output
ENUM_1_CONST_2 - Properties [value1=2, value2=goodbye, value3=7.54]
ENUM_2_CONST_1 - Properties [value1=9, value2=hey dude, value3=547.21578]
You could use composition instead of inheritance, putting the common code in a base component that becomes an attribute of your enums. Also this solution introduces a level of indirection, and requires 4 lines of boilerplate code for each new enum class.
Example:
/*
* Example of sharing common code between enum classes wityh same 'structure' using
* an EnumBase component.
* This is a work-around on the restriction that enum cannot inherit from other classes
*/
package provajava;
/**
*
* The base class wich contains the shared code
* In this example we want to define many enums
* which are composed of a field 'code' and a field 'name'
*/
class EnumBase {
public EnumBase( int code, String name ) {
theCode = code;
theName = name;
}
// The attributes can be declared final public
// In this case you do not need getters
// but you still make sure that they cannot be changed outside the constructor
final public int theCode;
final public String theName;
}
public class TestEnum2 {
/**
* A first enum class
*/
enum BOOLEAN {
TRUE ( 1, "True "),
FALSE (2, "False");
// This is boilerplate code that need to be repeated for each enum
// Annoying, but is much shorter than shared code, especially if there are many enum values
final public EnumBase base;
BOOLEAN (int c, String n) {
base = new EnumBase(c,n );
}
}
/**
* A second enum class
*/
enum NUMBER {
ONE ( 1, "One"),
TWO (2, "Two"),
THREE(3, "Three");
// This is boilerplate code that need to be repeated for each enum
// Annoying, but is much shorter than repeating shared code, especially if there are many enum values
final public EnumBase base;
NUMBER (int c, String n) {
base = new EnumBase(c,n );
}
}
public static void main(String args[] ) {
BOOLEAN b = BOOLEAN.FALSE;
NUMBER n = NUMBER.TWO;
System.out.println( b + "->" + b.base.theCode + "," + b.base.theName );
System.out.println( n + "->" + n.base.theCode + "," + n.base.theName );
// n.base.theCode = 2; // ERROR : cannot assign to a final variable
// n.base = new EnumBase( 1, "TWELVE"); // ERROR : cannot assign to a final variable
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With