如何在不写“if语句”的情况下初始化输入字段?

时间:2011-05-27 11:09:33

标签: java design-patterns if-statement enumeration

我有一个像

的枚举
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();
          }  ....
      }
}

10 个答案:

答案 0 :(得分:5)

听起来你的设计可能需要被关注。一些建议:

  • 将init方法添加到枚举中。所以 然后你可以遍历数组 您的枚举并调用init 对它的方法,所以枚举知道如何 做自己的初始化
  • 创建一个Command对象 初始化并创建一个 你的枚举地图作为关键和 命令作为值。周期 围绕运行Command的地图 每个枚举。
  • 使用反射 - 成本明智我不会太在意这一点,除非你的系统处于极低的延迟之后

对于第一个项目符号,您可以更改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。之后,您有三种选择:

  1. 也可以在其他一些数组中注册TextBox,
  2. 使用反射或
  3. 调用initA,...
  4. 使用一些仿函数构造来调用它们。
  5. 最佳选择取决于用例。

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

虽然这是一个非常简单的示例,但您可以稍后向构造函数添加任何代码,并根据实际对象本身做出更复杂的决策。