5

Let's me start from what I want to do then raising some questions I have.

I want to develop a general Java program which is a superset of a number of programs (let's call them program variants). In particular, the general program has methods which are only used by one or more program variants (but not all). Given a particular configuration, I want to remove unnecessary methods and just keep the smallest set of methods for one program variant.

For example, I have a general program as below:

public class GeneralProgram {

    // this method is common for all variants
    public void method1() {};

    // this method is specific to variant 1
    public void method2() {};

    // this method is specific to variant 2
    public void method3() {};
}

Then after pruning the program based on configuration for variant 1, the result is

public class GeneralProgram {

    // this method is common for all variants
    public void method1() {};

    // this method is specific to variant 1
    public void method2() {};
}

It doesn't matter if the resulting class name is the same as the original one or not. I just want to prune the content of the class.

So, here are my questions:

  1. Do you have any idea how to realize this except low level text processing?

  2. I know that I can use aspectJ to disable/enable specific methods at runtime but what I really want to do is performing this task before deploying the program. Is there any technique in Java for this purpose?

4

2 回答 2

6

在我看来,这里正确的解决方案是使用一些面向对象的编程并将您的程序分层:

base.jar 包含:

package foo.base;
class GeneralProgram {
   public void method1(){ }
}

var1.jar 包含:

package foo.var1;
import foo.base.GeneralProgram;
class GeneralProgramVar1 extends GeneralProgram {
   public void method2(){ }
}

var2.jar 包含:

package foo.var2;
import foo.base.GeneralProgram;
class GeneralProgramVar2 extends GeneralProgram {
   public void method3(){ }
}

一些部署将同时具有 base.jar 和 var1.jar,其他部署将具有 base.jar 和 var2.jar。您必须稍微弄乱类路径才能解决依赖关系。


如果你可以很好地分离你的变体,以便有真正未使用的函数,那么你可以使用像ProGuard这样的压缩实用程序从类中删除未使用的方法。但是,您可能会发现,获得 ProGuard 好处所需的努力与我上面推荐的结构相同。

于 2011-06-18T03:28:15.240 回答
3

@Mark Elliot 的回答为您提供了一个“正确的方法”来做到这一点。

一般而言,您的方式不是一个好主意的原因有很多,特别是对于 Java 应用程序:

  • Java 不支持这一点。具体来说,它不支持条件编译。

  • 虽然有时会使用源代码预处理器,但主流 Java 工具链并不支持它们。(对于(假设的?)在字节码级别运行的工具也是如此……尽管这不是您所说的。)

  • 使用条件编译变体,在一个变体中所做的更改更容易破坏另一个变体。(相比之下,一个好的 OO 设计会将特定于变体的代码隔离到特定类,它们不会影响其他变体的行为。)

  • 条件编译猖獗的代码库更难理解。

  • 条件编译变体使测试更加复杂。您基本上必须将每个变体视为必须单独测试的单独应用程序。这使得编写测试更加复杂,运行测试更加昂贵。(并且测试变体很重要,因为依赖条件编译的代码库很脆弱;见前文。)

  • 由于工具问题,测试覆盖分析更难/更多使用变体;见前文。


OP 在评论中写道:

因此,如果我为特定变体部署不必要的资源(例如,特定于其他变体的方法、类),它是无效的。

“无效”是什么意思?

在大多数情况下,代码库是否包含在某些用例或某些平台上未使用的功能并不重要。Java 应用程序使用大量内存,而代码大小通常不是造成这种情况的主要原因。简而言之,在大多数情况下,部署不会使用的代码是“有效的”:它完成了工作,开销并不重要

如果您有一个不寻常的应用程序,其中 JAR 文件大小或代码内存使用量非常重要(而不仅仅是假设问题),您仍然不需要求助于条件编译或字节码黑客。

  • 如果 JAR 文件大小是关键问题,那么有些工具会删除该工具确定不会使用的类和方法;例如,假设应用程序是从指定的main方法启动的。

  • 如果内存使用是关键问题,您可以构建代码,以便它使用动态加载来加载变体、平台甚至用例特定的类。

于 2011-06-18T06:43:11.670 回答