【问题标题】:Java - Pattern to recognize value typeJava - 识别值类型的模式
【发布时间】:2012-03-05 18:53:34
【问题描述】:

我正在为 Java 中的外部程序制作一个新的脚本生成器。这种语言支持变量,但它是一种无类型语言。这是我最初编写的代码示例:

public class Var
{
    private String name;
    private String type;
    private float defaultValue;
    private float lowerBound;
    private float upperBound;
    private float value;
    private LinkedList<Float> valuesConstraint;
    private String description;
    private Category category;
    private LinkedList<CvarDependency> dependencies;
    ...
}

var 类型通常是 Float,但也可以是 bool [0|1]、String 或 int。 所以我最终做出了这个实现:

abstract class Var
{
    private String name;
    ...
}

public class IntVar extends Var
{
    private int value;
    private int defaultValue;
    private int lowerBound; //-infinite
    private int upperbound; //+infinite
    ...
}

public class FloatVar extends Var
{
    private float value;
    private float defaultValue;
    private float lowerBound; //-infinite
    private float upperbound; //+infinite
    ...
}

public class StringVar extends Var
{
    private String value;
    private String defaultValue; //empty string
    ...
}

public class BoolVar extends Var
{
    private boolean value;
    private boolean defaultValue;
    private boolean lowerBound; //false <-> 0
    private boolean upperbound; //true  <-> 1
    ...
}

现在我必须将这些变量存储到 LinkedList 中,但是当我必须阅读其内容时,如何管理正确的转换?我读到使用这种方法不是一个好习惯:

Var var = Manager.getVar("namevar");
if( var.getClass().getName().equals("StringVar") )
    ...
else if( var.getClass().getName().equals("IntVar") )
    ...
else if( var.getClass().getName().equals("FloatVar") )
    ...
else if( var.getClass().getName().equals("BoolVar") )
    ...

有什么提示可以更好地处理这个问题吗?

【问题讨论】:

  • 我会为每个Var 建议一个Visitor 模式。这样,访问者就知道如何进行投射。
  • 好的,这就是我需要的模式。请将此评论作为答案发布,以便我可以将其放在回复堆栈的顶部!
  • 可以使用 instanceOf 运算符吗?
  • 当然可以,但我想要一个“干净”的实现!

标签: java object types casting


【解决方案1】:

与其让您的解释器直接提取和操作值,不如采用不同的方法并将操作定义为如下方法:

abstract class Var
{
    ...
    public Var add(Var var); // corresponds to    var + otherVar;   in script
    public Var subtract(Var var); // corresponds to    var - otherVar;   in script
    public Var unarySubtract(); // corresponds to    -var;   in script
    ...
}

这样,您可以通过调用这些方法来评估脚本中的表达式,并且您的类可以像这样覆盖和重载,例如:

public class IntVar extends Var
{
    ...
    public IntVar add(IntVar var)
    {
      return new IntVar(value + var.value); // add another int
    }
    public FloatVar add(FloatVar var)
    {
      return new FloatVar(value + var.value); // add another float (and cast result to float? up to you whether you want to do this...)
    }
    public StringVar add(StringVar var)
    {
      return new StringVar("" + value + var.value); // add a string, and cast to string (for string concatenation)
    }
    public Var add(Var var)
    {
      throw new OperationNotSupportedException(); // no other types can be added to an IntValue so throw an exception
    }
    public Var subtract(Var var) {...}
    public IntVar unarySubtract()
    {
      return new IntVar(-value);
    }
    ...
}

通过这种方式,您可以根据需要对它们实施和评估操作,而无需直接在解释器中处理值。

另一个好处是,使用此方法,如果用户尝试运行错误的脚本(例如尝试将 BoolVar 添加到 IntVar),则解释器中将引发异常。

也许你甚至可以在你的抽象 Var 类中使用默认的操作实现:

public Var add(Var var)
{
  throw new OperationNotSupportedException(); // no other types can be added to an IntValue so throw an exception
}

并且仅在安全使用操作的情况下覆盖/重载。

当您在这里说“阅读内容”时,大概至少在某个时候需要以统一的方式处理类型实例的内容,如果这只是为了输出,那为什么不直接一个抽象的“toString()”方法?如果出于输出以外的其他目的,那么您不能只使用其他一些 toX() 方法并以相同的方式处理每个类型实例的目的是什么?

我意识到这并不能直接回答您的问题,而是提出了一种不同的方法,以期完全避免该问题,这可能完全没有帮助,因为我可能对您试图实现的目标做出了一些愚蠢的假设 / o 已经了解了更多问题的背景,但我希望它对您有所帮助。

如果这没有帮助,请提供更多信息,我会尽力提供更多帮助,因为我不建议继续使用当前方法,但我不确定除此之外还有什么建议。

编辑:等等,我的错,我没有正确阅读问题。这不是解释器,而是生成器。什么样的发电机?什么语言的脚本生成器?好吧,这些可能不是重要的问题,但现在我重新阅读了这个问题,我意识到我不太了解您需要实现的目标。

我仍然认为封装是比访问者模式更好的选择,但总的来说:)。当然,除非出于某种原因需要添加可以对任何变量进行操作的行为,否则无法通过 Var 提供的接口从已经附加到每个变量的行为构造行为... 什么行为可能这是?对于输出,无论如何你肯定需要每个变量都以它们共同的格式可读,为什么不只是 toString()、toSomething()、toAnotherThing() 等。

【讨论】:

    【解决方案2】:

    我的建议是使用Visitor 模式。该算法将放置在 Visitor 中,而不是放在对象 Var(和子类)中。

    public class Var {
    
        public void accept(VarVisitor visitor) {
            visitor.visit(this);
        }
    }
    

    访客

    public interface VarVisitor {
        public void visit(FloatVar var);
        public void visit(IntVar var);
        public void visit(StringVar var);
        public void visit(BoolVar var);
    
        //...etc.
        public Object getValue();
    }
    

    VisitorImpl

    public class VarVisitorImpl implements VarVisitor {
        private Object value;
    
        @Override
        public Object getValue() {
            return value;
        }
    
        @Override
        public void visit(FloatVar var) {
    
        }
    
        @Override
        public void visit(IntVar var) {
    
        }
    
        @Override
        public void visit(StringVar var) {
    
        }
    
        @Override
        public void visit(BoolVar var) {
    
        }
    }
    

    我希望这能让您对想要实现的目标有所了解。

    【讨论】:

      【解决方案3】:

      您可以使用泛型类型,但我不知道您需要对成员做什么...

      public class Var<T extends Comparable<T>>
      {
          private String name;
          private String type;
          private T defaultValue;
          private T lowerBound;
          private T upperBound;
          private T value;
          private LinkedList<T> valuesConstraint;
          private String description;
          private Category category;
          private LinkedList<CvarDependency> dependencies;
          ...
      }
      

      【讨论】:

      • 事实上我必须进行一些操作。 int、bool 和 float 的 defaultValue、lowerBound、upperBound 很容易管理,但 Strings 则不然!
      【解决方案4】:

      不比较类名,比较类:

      if( var.getClass().equals(IntVar.class) ) {
        IntVar intVar = (IntVar) var; 
        ...
      

      您也可以使用var instanceof IntVar,但如果您编写一个扩展IntVar 的类,这可能会导致问题。

      【讨论】:

      • 是的。但在比较之前,你应该检查 var 不是null
      猜你喜欢
      • 1970-01-01
      • 2018-07-26
      • 2014-07-08
      • 2011-04-09
      • 1970-01-01
      • 2011-06-02
      • 2015-07-13
      • 2013-05-30
      相关资源
      最近更新 更多