我有一个像
的枚举public enum Field {
A, B, C, D, E ....;
private Field(){
}
}
我有一个class Panel
,需要Field array
来初始化字段:
public class Panel {
TextBox A;
TextBox B;
TextBox C;
TextBox D;
TextBox E;
...
public Panel(Field[] fields){
this.fields = fields;
init();
}
public void initA(){}
public void initB(){}
public void initC(){}
public void initD(){}
public void initE(){}
}
我的问题是,如何在不编写if语句的情况下初始化给定的字段?
我找不到任何解决方案,我现在正在初始化:
public void init(){
for(int i = 0 ; i < fields.length; i++){
if(fields[i] == Field.A){
initA();
} else if(fields[i] == Field.B){
initB();
} else if(fields[i] == Field.C){
initC();
} else if(fields[i] == Field.D){
initD();
} else if(fields[i] == Field.E){
initE();
} ....
}
}
答案 0 :(得分:5)
听起来你的设计可能需要被关注。一些建议:
对于第一个项目符号,您可以更改TextBox以保留Field类型,例如
TextBox A = new TextBox(Field.A);
TextBox B = new TextBox(Field.B);
因此,如果TextBox知道它是A,B,C,D,E那么你只需要在你的Field []周围循环,当它找到它的东西时TextBox运行init代码(可以针对特定的枚举实例存储) )。当然,您需要在某个地方的数据结构中注册所有TextBox实例,因为您似乎非常反对使用非常广泛使用的反射API。
本质上,Field和TextBox之间必须有一个链接。如果没有你的说法,Java就无法读懂你的想法。好吧,至少在Google推出他们的心灵感应API之前(这可能只适用于Go ......)。这可以基于命名(反射),硬编码逻辑(ifs或开关)或基于状态来完成。对于后者,这意味着将Field与TextBox相关联,正如我在上面的构造函数示例中所演示的那样。
答案 1 :(得分:1)
从设计角度来看,我会选择工厂模式,单例模式(基于枚举)和命令模式的组合。我看到一组命令,其中每个命令都特定于给定值。 factory ( Singleton )是创建此类专用实例的常用模式。 即使它只是将if / switch链移动到工厂中(但允许工厂使用条件检查来创建实例..)。
// the init command
public interface PanelInitializer {
public init(Panel p);
}
// the factory
public enum PanelInitializerFactory {
INSTANCE;
public PanelInitializer create(Field field) {
switch (field) {
case A: return new TypeAInitializer();
case B: return new TypeBInitializer();
case C: return new TypeCInitializer();
//..
}
}
}
我不认为我们可以在不使用命名约定和反射/实例化或不引入约束的情况下摆脱所有条件检查,所有初始化程序共享相同的代码。
答案 2 :(得分:1)
正如@planetjones所提到的,您可以在枚举类中添加init()
方法。 init方法应该返回对其(枚举)类型的初始化TextBox
的引用。如果您需要将数据传递给initialisor,您可以传递this
,以便它可以检索所需的任何信息。
要解决找到要分配的变量的问题,您可以声明TextBox
es
public void init(){
for(int i = 0 ; i < fields.length; i++){
F[i] = fields[i].init(this);
}
}
或在初始化临时数组后分配它们。
public void init(){
TextBox F[5];
for(int i = 0 ; i < fields.length; i++){
F[i] = fields[i].init(this);
}
A = F[0];
B = F[1];
C = F[2];
D = F[3];
E = F[4];
}
当然你应该声明常数而不是使用幻数。
答案 3 :(得分:1)
这是一个将init方法添加到枚举的片段。在每个Field的init方法中,您可以调用一个不同的initX()方法。使init方法变为抽象,编译器会提醒您为枚举值定义init方法。
enum Field
{
A{public void init(){initA();}},
B{public void init(){initB();}},
C{public void init(){initC();}},
public abstract void init();
}
答案 4 :(得分:0)
你可以使用java反射来循环你的枚举,但你真的应该考虑一些方法来整合你所有的initN()方法。
答案 5 :(得分:0)
例如,您可以通过将初始化逻辑移动到枚举来实现。有一个方法初始化,将TextBox作为参数并初始化它。
顺便说一句,你最好在数组中使用TextBox变量。
修改强> 另一个选择是,我经常使用枚举作为一种原型存储。我有一个方法返回一个匹配某个枚举类型的对象。
如果您不想在枚举中进行初始化,可以将其移动到要返回的对象。对于每个特定对象,您将有一个单独的初始化。
我担心你是想用这个来追逐龙。 以这种方式看待它。由于你的问题是'条件',即你必须根据字段的枚举类型进行不同的初始化,因此在某些时候你必须使用ifs或switch语句。
相信没有神奇的方法,程序应该如何知道你想做什么?即使使用反射,您也会使用ifs等对其进行相应的初始化。
答案 6 :(得分:0)
为什么你当前的实施很糟糕?只是因为它看起来“丑陋”?您可以使用开关而不是一堆if:
public void init(){
for(int i = 0 ; i < fields.length; i++){
switch(fields(i)){
case A:
initA();
break
case B:
...
}
}
}
也许initA中的逻辑,initB ......非常相似?如果你有20个不同的枚举,20个不同的init运行,没有太大的改进空间......
答案 7 :(得分:0)
如果与每个TextBox关联的init方法非常不同,并且Fields列表很小,那么您的方法完全没有问题。此外,如果您通常只实例化其中一个Panel实例,那么其他方法实际上可能不仅仅是帮助。
话虽如此,请考虑使用java.util.EnumMap。之后,您有三种选择:
initA
,...
最佳选择取决于用例。
答案 8 :(得分:0)
从这个例子开始:
import java.util.HashMap;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class FooPanelMain {
public static void main(String[] args) {
FooPanel panel = new FooPanel();
JFrame frame = new JFrame();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
class FooPanel extends JPanel {
// fields are dynamically created, so we put them into a map
private Map<PanelField, JLabel> fields = new HashMap<PanelField, JLabel>();
// enum to configure the fields
private enum PanelField {
FIRST("first text"),
SECOND("second text"),
LAST("last text");
private String text;
private PanelField(String text) {
this.text = text;
}
public String getLabelName() {
return text;
}
}
// constructor uses the enum configuration to create the fields
public FooPanel() {
for (PanelField fooPanelField : PanelField.values()) {
createLabel(fooPanelField);
}
}
private void createLabel(PanelField field) {
JLabel label = new JLabel(field.getLabelName());
this.add(label);
fields.put(field, label);
}
}
通过定义PanelField的接口,可以很容易地将此示例转换为抽象解决方案,该接口由枚举实现。 FooPanel可以用作Panels的基类。
答案 9 :(得分:-1)
最好将init移动到枚举中,就像:
public enum Example{
value1(1),
value2(2),
value3(66);
private final int internalValue;
Example(int value){
this.internalValue = value;
}
public int getInternalValue(){
return this.internalValue;
}
}
虽然这是一个非常简单的示例,但您可以稍后向构造函数添加任何代码,并根据实际对象本身做出更复杂的决策。