继承公共接口的枚举中的代码重复
Code duplication in enums inheriting a common interface
我有几个符合通用接口的枚举:
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 这几乎是不可能的,因为枚举不能扩展实现。
我在这里需要的是一个建议或一个不同的策略,可以用更干净的方式完成。是否已经有一些关于此的现有模式?
您的所有枚举似乎都是单一值。您应该将它们组合成一个 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
}
如果您可以接受一级间接,那么我在下面介绍的方法会将重复代码减少到最低限度。
首先,考虑以下供应商接口及其内部 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
+ "]";
}
}
}
这里没有魔法。内部 class 只是一个带有 private final
字段的 bean,一个初始化它们的私有构造函数,public getters,一个工厂方法和一个覆盖的 toString()
方法.该接口只定义了一个方法,即returns内部class的一个实例。请注意,内部 class 是最终的。这个想法是强制不变性,因此不允许更改其属性。
然后,让我们创建几个将实现此接口的枚举。让我们从 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]
您应该考虑使用 class 并实现此方法以获取所有常量值,就像枚举一样:
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;
}
}
示例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());
}
}
您可以使用组合而不是继承,将公共代码放在成为枚举属性的基本组件中。
此解决方案还引入了一个间接级别,并且每个新枚举需要 4 行样板代码 class.
示例:
/*
* 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
}
}
我有几个符合通用接口的枚举:
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 这几乎是不可能的,因为枚举不能扩展实现。 我在这里需要的是一个建议或一个不同的策略,可以用更干净的方式完成。是否已经有一些关于此的现有模式?
您的所有枚举似乎都是单一值。您应该将它们组合成一个 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
}
如果您可以接受一级间接,那么我在下面介绍的方法会将重复代码减少到最低限度。
首先,考虑以下供应商接口及其内部 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
+ "]";
}
}
}
这里没有魔法。内部 class 只是一个带有 private final
字段的 bean,一个初始化它们的私有构造函数,public getters,一个工厂方法和一个覆盖的 toString()
方法.该接口只定义了一个方法,即returns内部class的一个实例。请注意,内部 class 是最终的。这个想法是强制不变性,因此不允许更改其属性。
然后,让我们创建几个将实现此接口的枚举。让我们从 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]
您应该考虑使用 class 并实现此方法以获取所有常量值,就像枚举一样:
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;
}
}
示例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());
}
}
您可以使用组合而不是继承,将公共代码放在成为枚举属性的基本组件中。 此解决方案还引入了一个间接级别,并且每个新枚举需要 4 行样板代码 class.
示例:
/*
* 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
}
}