代码之家  ›  专栏  ›  技术社区  ›  Daniel Kaplan

如何生成源代码以创建正在调试的对象?

  •  7
  • Daniel Kaplan  · 技术社区  · 14 年前

    我的典型场景:

    1. 我所处理的旧代码有一个bug,只有生产中的客户机有这个bug。
    2. 我附加了一个调试器,并解决了如何在 他们的 系统给出 他们的 输入。但是,我还不知道为什么会发生这种错误。
    3. 现在我想在本地系统上编写一个自动测试来尝试复制,然后修复这个错误。

    最后一步真的很难。输入可能非常复杂,并且有很多数据。手工创建输入(例如: P p = new P(); p.setX("x"); p.setY("x"); 想象一下这样做1000次来创建对象)是非常繁琐和容易出错的。实际上,你可能会注意到我刚才给出的例子中有一个拼写错误。

    有没有一种自动的方法可以从我的调试器中的断点获取字段,并生成将创建该对象的源代码,以相同的方式填充?

    我唯一想到的就是序列化这个输入(例如,使用XStream)。我可以将它保存到一个文件中,然后在自动测试中读取它。这有一个主要的问题:如果类以某种方式改变(例如:字段/getter/setter名称被重命名),我将无法再反序列化对象。换句话说,测试是非常脆弱的。

    2 回复  |  直到 12 年前
        1
  •  2
  •   AlexWien    12 年前

    众所周知,Java标准序列化在对象更改其版本(字段的内容、命名)时不太有用。对于快速演示项目来说很好。

    更适合您的需求的方法是objetcs支持您自己的(二进制)自定义序列化:
    这不难,使用 DataOutputStream 写出一个对象的所有字段。但是现在介绍versiong,首先写出一个 versionId . 对象只有一个版本,请写出versionID 1。这样以后,当您必须在对象中引入更改时,可以删除字段、添加字段、提高版本号。

    这样的 ICustomSerializable 然后将首先在readObject()方法中从输入流中读取版本号,并根据版本ID调用readVersionv1()或 readVersionV2().

    public Interface ICustomSerializable {
      void writeObject(DataOutputStream dos);
      Object readObject(DataInputStream dis);
    }
    
    public Class Foo {
      public static final VERSION_V1 = 1;
      public static final VERSION_V2 = 2;
    
      public static final CURRENT_VERSION = VERSION_V2;
      private int version;
    
      private int fooNumber;
      private double fooDouble;
    
      public void writeObject(DataOutputStream dos) {
         dos.writeInt(this.version);
          if (version == VERSION_V1) {
              writeVersionV1(dos);
          } else (version == VERSION_V2) {
              writeVersionV2(dos);
          } else {
             throw new IllegalFormatException("unkown version: " + this.version);
          }
      }
      public void writeVersionV1(DataOutputStream dos) {
            writeInt(this.fooNumber);
            writeDouble(this.fooValue);
      }
    }
    

    需要进一步的getter和setter,以及将版本初始化为当前版本的构造函数。

    如果您更改或添加适当的读写版本,那么这种序列化可以安全地重构。对于使用外部libs类而不是控件的复杂对象,它可以做更多的工作,但是字符串、列表很容易序列化。

        2
  •  1
  •   blackdrag    12 年前

    我认为您要做的是存储“状态”,然后在测试中恢复它,以确保bug保持不变。

    简而言之:阿法克没有这样的通用代码生成工具,但是只要保持几个约束条件,编写这样的工具是很小的工作。

    长评论: 有一些约束条件可以发挥作用。如果所有的东西都只是bean,其中包含所有需要的字段的getter和setter,那么为它生成代码就不那么困难了。是的,如果您将生成的代码与普通代码一起重构,那么重命名是安全的。如果缺少setter,则此方法将不起作用。这只是为什么这不是一般解决方案的一个例子。

    例如,重构还可以将字段移动到其他类。您希望如何从该类的其他字段引入值?你以后怎么知道那些改变了你保存状态的数据是否仍然反映了关键数据?或者更糟的是,假设重构给同一字段赋予了与以前不同的含义。

    bug本身的性质也是一个约束。例如,假设发生错误的原因是字段/方法具有这个名称和那个名称。如果重构现在更改了名称,那么无论您的状态如何,bug都将不再出现。

    这些只是武断的例子,可能与你的现实生活完全无关。但这是一个个案决策,而不是一般策略。不管怎样,如果你知道你的代码,bug和重构都能很好地完成这个任务,那么制造这样一个工具只需要不到一天的时间,可能要少得多。

    使用XStream,您也可以部分地得到它,但是您必须自己更改XML。如果您使用例如db4o,您必须告诉它这个和那个字段现在有这个和那个名称。