Java 创建多个全局常量的正确方法?
我使用的是一种旧的应用程序,其中有大量的全局枚举/类,这些枚举/类声明常量键/类声明常量键/值集 这些值中的许多基本上是多余的。。。相同的基本常量在不同的枚举中以稍微不同的名称声明,在每一个枚举中它都与一组不同的值相关联 通常情况下,此类类引用其他类似大型类中的值,以创建某些常量层次结构。。。正如您在下面的示例中所看到的 定义这些常数的正确方法是什么?他们应该像现在这样硬编码吗 例如:Java 创建多个全局常量的正确方法?,java,design-patterns,anti-patterns,Java,Design Patterns,Anti Patterns,我使用的是一种旧的应用程序,其中有大量的全局枚举/类,这些枚举/类声明常量键/类声明常量键/值集 这些值中的许多基本上是多余的。。。相同的基本常量在不同的枚举中以稍微不同的名称声明,在每一个枚举中它都与一组不同的值相关联 通常情况下,此类类引用其他类似大型类中的值,以创建某些常量层次结构。。。正如您在下面的示例中所看到的 定义这些常数的正确方法是什么?他们应该像现在这样硬编码吗 例如: public class ParameterSet { //hundreds of similar
public class ParameterSet {
//hundreds of similar declarations..........
public static ParameterTypeAbstract BREADCRUMB1_BREADCRUMB2_BREADCRUMB3_BREADCRUMB4_452 = new ParameterTypeSet(
KeywordType.PARAMETER_OF_SOME_TYPE_26659,
ParameterGroupType.PARAMETER_OF_SOME_OTHER_TYPE_967347,
ParameterScopeType.ACCOUNT_TYPE_557.ACCOUNT_SUB_TYPE_33791.getStringCode(), ParameterScopeImportantType.getStringCodes(),
"Some description",
true, true, true, null, null);
public static ParameterTypeAbstract BREADCRUMB1_BREADCRUMB2_BREADCRUMB3_BREADCRUMB4_453 = new ParameterTypeSet(
KeywordType.PARAMETER_OF_SOME_TYPE_90689,
ParameterGroupType.PARAMETER_OF_SOME_OTHER_TYPE_867335,
ParameterScopeType.ACCOUNT_TYPE_538.ACCOUNT_SUB_TYPE_48224.getStringCode(), ParameterScopeImportantType.getStringCodes(),
"Some other description",
true, true, true, null, null);
//hundreds of similar declarations..........
}
以下是我过去使用过的一些策略。如果我知道你要解决的实际问题,坦白地说,我对面包屑的理解不太清楚,那么回答起来就容易多了。为了给出一个说明性的答案,我将假设以下示例:假设我们想要制作一些图形并使用调色板。我们需要几个调色板,例如,每个调色板都有一组有限的命名颜色。我们的要求是: 为常量的嵌套结构提供一致的接口。 提供静态类型安全的方式来引用调色板中的颜色。 提供一种方便且最好是高效的方法来按名称查找运行时调色板和颜色。 提供一种方便且最好是高效的方法,在运行时枚举所有调色板和颜色。 首选外部配置源。 我们希望通过不可变颜色类的实例来表示颜色,该类具有以压缩RGB值为参数的构造函数 具有公共静态常量的嵌套类 这些类很简单,可以作为构建过程的一部分轻松生成。我个人喜欢将这些数据保存在XML文件中,并使用XSLT样式表从中生成Java或任何代码。我已经“禁用”了类的构造函数,并将它们声明为final,因为只有静态成员的类既不应该实例化也不应该子类化 让我们看看这个解决方案是否符合我们的要求 如果我们想在调色板中引用一种颜色,并在编译时知道需要哪种颜色,我们可以这样做:
Color color1 = Palettes.Tango.ORANGE_LIGHT;
这给了我们静态类型检查;如果我们不小心引用了一个不存在的颜色或调色板,或者拼错了一个名称,编译器会告诉我们
Color color2 = Palettes.Gonme.GREEN_HIGHLIGHT; // compile-time error
Color color3 = Palettes.Gnome.GREEN_HIHGLIGHT; // compile-time error
如果我们在运行时从用户那里得到一个调色板和颜色名称,并希望查找它,该怎么办?这是不方便的。毫无疑问,下面的解决方案是一个糟糕的解决方案,即使代码可以很容易地从与类相同的数据源生成
public Color selectColor(final String palette, final String color) {
switch (palette) {
case "Tango":
switch (color) {
case "Butter Light": return Palettes.Tango.BUTTER_LIGHT;
case "Butter Medium": return Palettes.Tango.BUTTER_MEDIUM;
case "Butter Dark": return Palettes.Tango.BUTTER_DARK;
case "Orange Light": return Palettes.Tango.ORANGE_LIGHT;
case "Orange Medium": return Palettes.Tango.ORANGE_MEDIUM;
// case ...
default: throw new IllegalArgumentException(String.format(
"There is no color '%s' in palette '%s'", color, palette));
}
case "Gnome":
switch (color) {
case "Basic 3D Highlight": return Palettes.Gnome.BASIC_3D_HIGHLIGHT;
case "Basic 3D Dark": return Palettes.Gnome.BASIC_3D_DARK;
case "Green Highlight": return Palettes.Gnome.GREEN_HIGHLIGHT;
// case ...
default: throw new IllegalArgumentException(String.format(
"There is no color '%s' in palette '%s'", color, palette));
}
// case ...
default:
throw new IllegalArgumentException(String.format(
"There is no palette '%s'", palette));
}
}
另一种选择是使用反射,反射可能更好,但仍然是设计选择不佳的指标
枚举所有选项板/颜色也是如此
嵌套词典
但是,这些现在将是运行时错误:
Color color2 = GlobalConstants.COLOR_PALETTES.get("Gonme").get("Green Highlight"); // NullPointerException
Color color3 = GlobalConstants.COLOR_PALETTES.get("Gnome").get("Green Hihglight"); // color3 == null, surprise will come later
在运行时查找调色板和颜色现在变得很简单,因为我们的方法与编译时常量的方法一样有效
public Color selectColor(final String palette, final String color) {
final Map<String, Color> thePalette;
final Color theColor;
thePalette = GlobalConstants.COLOR_PALETTES.get(palette);
if (thePalette == null)
throw new IllegalArgumentException(String.format(
"There is no palette '%s'", palette));
theColor = thePalette.get(color);
if (theColor == null)
throw new IllegalArgumentException(String.format(
"There is no color '%s' in palette '%s'", color, palette));
return theColor;
}
这也是代码生成的候选者。可能最令人不快的是,我们必须重复将颜色与每个调色板中的枚举相关联的逻辑。在本例中,它看起来并没有那么糟糕,但如果我们需要比简单RBG值更多的数据,可能还需要其他方便的方法,情况会变得更糟
否则,这个解决方案看起来几乎完美,除了一个主要缺点:我们不能嵌套枚举。至少,我不知道怎么做。这意味着它实际上是上述两种方法的混合
以下是我们在编译时如何引用常量:
Color color1 = TangoPalette.ORANGE_LIGHT.asColor();
Color color2 = GonmePalette.GREEN_HIGHLIGHT.asColor(); // compile-time error
Color color3 = GnomePalette.GREEN_HIHGLIGHT.asColor(); // compile-time error
这再次得益于完整的静态类型检查。不得不写作。asColor不好,但在我看来只是一个小麻烦
在运行时查找颜色的方便性介于上述两种方法之间。我们仍然需要编写自己的逻辑来选择调色板,但一旦有了它,我们就可以使用valueOf方法
同样,从给定调色板中枚举颜色只需使用values方法即可
如果我们能用所有选项板的名称创建一个额外的枚举就好了
结论
我喜欢枚举,所以最后一个解决方案最吸引我。通常,我会使用以下经验法则:
如果需要递归嵌套,请使用第一种方法处理带有公共静态常量的嵌套类。
如果将来从外部数据库加载值似乎不可信,请考虑使用字典。
但是,如果大多数值在编译时已经为静态类型检查所知,那么最好远离字典。
使用枚举,特别是当您只需要一级递归时。
你的常数应该是最终的,否则。。。。;好吧……但这似乎更像是一个惯例,而不是一个真正的常数。我认为这很重要。A.
常数必须是一个常数,否则它显然不是常数。无论如何,除了硬编码之外,还有什么方法可以解决你的观点?因为代码在爪哇,所以C++ C++标签没有被删除,也没有关于C++的请求或讨论。你现在如何使用这些“常量”?如果像你说的那样有成百上千的支票,你还有其他的支票吗?如果我能想象这些参数有什么好处以及如何使用,我就更容易给出提示。
Color color2 = GlobalConstants.COLOR_PALETTES.get("Gonme").get("Green Highlight"); // NullPointerException
Color color3 = GlobalConstants.COLOR_PALETTES.get("Gnome").get("Green Hihglight"); // color3 == null, surprise will come later
public Color selectColor(final String palette, final String color) {
final Map<String, Color> thePalette;
final Color theColor;
thePalette = GlobalConstants.COLOR_PALETTES.get(palette);
if (thePalette == null)
throw new IllegalArgumentException(String.format(
"There is no palette '%s'", palette));
theColor = thePalette.get(color);
if (theColor == null)
throw new IllegalArgumentException(String.format(
"There is no color '%s' in palette '%s'", color, palette));
return theColor;
}
enum TangoPalette {
BUTTER_LIGHT(new Color(0xFCE94F)),
BUTTER_MEDIUM(new Color(0xEDD400)),
BUTTER_DARK(new Color(0xC4A000)),
ORANGE_LIGHT(new Color(0xFCAF3E)),
ORANGE_MEDIUM(new Color(0xF57900));
// Many more Tango colors...
private final Color color;
TangoPalette(final Color color) {
this.color = color;
}
public Color asColor() {
return this.color;
}
}
enum GnomePalette {
BASIC_3D_HIGHLIGHT(new Color(0xEAE8E3)),
BASIC_3D_DARK(new Color(0x807D74)),
GREEN_HIGHLIGHT(new Color(0xC5D2C8));
// Many more Gnome colors...
private final Color color;
GnomePalette(final Color color) {
this.color = color;
}
public Color asColor() {
return this.color;
}
}
// Many more palettes...
Color color1 = TangoPalette.ORANGE_LIGHT.asColor();
Color color2 = GonmePalette.GREEN_HIGHLIGHT.asColor(); // compile-time error
Color color3 = GnomePalette.GREEN_HIHGLIGHT.asColor(); // compile-time error
public Color selectColor(final String palette, final String color) {
final String mangledColor = color.replace(' ', '_').toUpperCase();
switch (palette) {
case "Tango":
return TangoPalette.valueOf(mangledColor).asColor();
case "Gnome":
return GnomePalette.valueOf(mangledColor).asColor();
// case ...
default:
throw new IllegalArgumentException(String.format(
"There is no palette '%s'", palette));
}
}