继承公共接口的枚举中的代码重复

时间:2015-07-17 09:54:57

标签: java design-patterns enums

我有几个符合常用界面的枚举:

    interface TableColumns
    {
      String getColumnName();
      int getColumnIndex();
      ColumnType getColumnType();
      boolean isEditable();
      int getColumnWidth();
    }

典型的实现是:

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;
    }
}

我有几个这样的实现(10+),每个都有多个几乎不同的值。现在,问题是我在这里看到了很多代码重复,因为所有实现中的方法都是逐字相同的。我知道在Java中这几乎是不可能的,因为枚举不能扩展实现。 我需要的是一个建议或不同的策略,可以更清洁地完成。是否已有一些关于此的模式?

4 个答案:

答案 0 :(得分:4)

如果您可以使用一个级别的间接,那么我在下面介绍的方法会将重复的代码减少到最小。

首先,考虑以下供应商界面及其内部类:

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
                    + "]";
        }
    }
}

这里没有什么神奇之处。内部类只是一个带有private final字段的bean,一个用于初始化它们的私有构造函数,公共getter,一个工厂方法和一个覆盖的toString()方法。该接口仅定义一个返回内部类实例的方法。请注意,内部类是最终的。我们的想法是强制实现不变性,以便不允许其属性发生变化。

然后,让我们创建一些将实现此接口的枚举。让我们从MyEnum1开始,它定义了两个值:

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;
    }
}

接下来是MyEnum2,它只定义了一个值:

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;
    }
}

如您所见,两个枚举都实现了PropertiesSupplier接口,因此它们必须为Properties properties()方法提供实现。为了符合这一点,他们必须封装他们在构造函数中收到的Properties实例。

所以现在,在这个间接之后,在所有枚举中重复的唯一代码就是Properties字段,作为参数接收它的构造函数及其getter方法。

这是一个展示如何使用枚举的示例:

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());

此代码生成以下输出

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]

答案 1 :(得分:0)

您的所有枚举似乎都是单个值。您应该将它们合并为一个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
}

答案 2 :(得分:0)

您应该考虑使用类并实现此方法以获取所有常量值,如枚举:

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;
    }
}

示例类:

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());
    }
}

答案 3 :(得分:0)

您可以使用合成而不是继承,将公共代码放在基础组件中,该基础组件将成为枚举的属性。 此解决方案还引入了一个间接级别,并且每个新的枚举类需要4行样板代码。

示例:

/*
 * 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

    }

}