Code duplication in enums inheriting a common interface

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]

All of your enums appear to be single values. You should combine them into a single enum

enum TableColumns {

  POINTS("points_column", false, ColumnType.TEXT, 400, 0),
  FOO("foo_column", true, ColumnType.TXT, 123, 0),
  BAR("bar_column", true, ColumnType.TXT, 123, 0)
  ;
  ...rest is the same as your old POINTS_COLUMN
}

You should consider using class and implement this method to get all constant values as enum does:

public class Utils {
    public static <T> List<T> values() {
        java.util.List<T> result = new ArrayList<>();
        Field[] fields = Bike.class.getFields();
        for (Field field : fields) {
            if (field.getModifiers() == (Modifier.PUBLIC ^ Modifier.STATIC ^ Modifier.FINAL) && field.getType().equals(Bike.class)) {
                try {
                    result.add((T) field.get(null));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
}

Example class:

public class Bike {
    public final static Bike
            HONDA = new Bike("Honda"),
            SUZUKI = new Bike("Suzuki"),
            YAMAHA = new Bike("Yamaha");

    private String name;

    public Bike(String name) {
        this.name = name;
    }

    public static List<Bike> values() {
        return Utils.<Bike>values();
    }

    public static void main(String[] args) {
        System.out.println(values());
    }
}
链接地址: http://www.djcxy.com/p/86784.html

上一篇: 如何调用Koa.js应用程序实例进行单元测试

下一篇: 枚举中的代码重复继承一个通用接口