5 Stimmen

Wie man ein Java-Programm beschneidet

Lassen Sie mich damit beginnen, was ich tun möchte und dann einige Fragen stellen, die ich habe.

Ich möchte ein allgemeines Java-Programm entwickeln, das eine Obermenge einer Reihe von Programmen (nennen wir sie Programmvarianten) ist. Insbesondere hat das allgemeine Programm Methoden, die nur von einer oder mehreren Programmvarianten (aber nicht von allen) verwendet werden. Bei einer bestimmten Konfiguration möchte ich unnötige Methoden entfernen und nur die kleinste Menge von Methoden für eine Programmvariante behalten.

Ich habe zum Beispiel ein allgemeines Programm wie unten:

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() {};
}

Nach der Beschneidung des Programms auf der Grundlage der Konfiguration für Variante 1 lautet das Ergebnis dann

public class GeneralProgram {

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

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

Es spielt keine Rolle, ob der resultierende Klassenname mit dem ursprünglichen Namen übereinstimmt oder nicht. Ich möchte nur den Inhalt der Klasse bereinigen.

Also, hier sind meine Fragen:

  1. Haben Sie eine Idee, wie man dies realisieren kann, abgesehen von der Textverarbeitung auf niedriger Ebene?

  2. Ich weiß, dass ich aspectJ verwenden kann, um bestimmte Methoden zur Laufzeit zu deaktivieren/aktivieren, aber was ich wirklich tun möchte, ist die Durchführung dieser Aufgabe vor der Bereitstellung des Programms. Gibt es eine Technik in Java für diesen Zweck?

6voto

Mark Elliot Punkte 71774

Mir scheint, dass die richtige Lösung hier darin besteht, objektorientierte Programmierung zu verwenden und Ihr Programm zu schichten:

base.jar enthält:

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

var1.jar enthält:

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

var2.jar enthält:

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

Bei einigen Installationen werden sowohl base.jar als auch var1.jar vorhanden sein, bei anderen base.jar und var2.jar. Sie müssen ein wenig mit den Klassenpfaden herumspielen, um die Abhängigkeiten aufzulösen.


Wenn Sie Ihre Varianten gut genug trennen können, so dass es wirklich keine ungenutzten Funktionen gibt, können Sie ein Komprimierungsprogramm wie ProGuard um ungenutzte Methoden aus den Klassen zu entfernen. Möglicherweise werden Sie jedoch feststellen, dass der Aufwand, der erforderlich ist, um die Vorteile von ProGuard zu nutzen, der gleichen Struktur entspricht, die ich oben empfohlen habe.

3voto

Stephen C Punkte 665668

Die Antwort von @Mark Elliot gibt Ihnen eine "richtige" Vorgehensweise vor.

Es gibt eine Reihe von Gründen, warum Ihr Weg im Allgemeinen und für Java-Anwendungen im Besonderen keine gute Idee ist:

  • Java unterstützt dies nicht. Insbesondere unterstützt es keine bedingte Kompilierung.

  • Zwar werden manchmal Quellcode-Präprozessoren verwendet, aber die gängigen Java-Werkzeugketten unterstützen sie nicht. (Dasselbe gilt für (hypothetische?) Werkzeuge, die auf der Bytecode-Ebene arbeiten ... obwohl das nicht das ist, worüber Sie zu sprechen scheinen).

  • Bei Varianten mit bedingter Kompilierung kann eine Änderung in einer Variante leichter zu einem Bruch in einer anderen führen. (Im Gegensatz dazu wird ein guter O-O-Entwurf variantenspezifischen Code in bestimmten Klassen isolieren, wo er das Verhalten anderer Varianten nicht beeinflussen kann.)

  • Eine Codebasis mit zügelloser bedingter Kompilierung ist viel schwieriger zu verstehen.

  • Bedingte Kompilierungsvarianten machen das Testen komplizierter. Im Grunde müssen Sie jede Variante als eine separate Anwendung behandeln, die separat getestet werden muss. Das macht das Schreiben von Tests komplizierter und die Durchführung von Tests teurer. (Und das Testen der Varianten IST wichtig, weil Codebasen, die auf bedingter Kompilierung beruhen, anfällig sind; siehe oben).

  • Die Analyse der Testabdeckung ist bei Varianten schwieriger/mehr Arbeit, weil es Probleme mit den Werkzeugen gibt; siehe oben.


In einem Kommentar schreibt der OP:

Es ist also nicht effektiv, wenn ich unnötige Ressourcen (z.B. Methoden, Klassen, die für andere Varianten spezifisch sind) für eine bestimmte Variante einsetze.

Was meinen Sie mit "nicht wirksam" ?

In den meisten Fällen spielt es einfach keine Rolle, dass eine Codebasis Funktionen enthält, die in bestimmten Anwendungsfällen oder auf bestimmten Plattformen nicht verwendet werden. Java-Anwendungen verbrauchen viel Speicher, und die Codegröße ist im Allgemeinen nicht die Hauptursache dafür. Zusammengefasst, in den meisten Fällen Es ist "effektiv", Code einzusetzen, der nicht verwendet wird: Er erfüllt seine Aufgabe, und die Gemeinkosten fallen nicht an. wirklich Materie.

Wenn Sie eine solche haben Ungewöhnlich Anwendungen, bei denen die Größe der JAR-Datei oder der Speicherverbrauch des Codes wirklich von Bedeutung ist (und nicht nur ein hypothetisches Problem), müssen Sie trotzdem nicht auf bedingte Kompilierung oder Bytecode-Hacking zurückgreifen.

  • Wenn die Größe der JAR-Datei ausschlaggebend ist, gibt es Werkzeuge, die Klassen und Methoden herausnehmen, die nicht verwendet werden, z. B. unter der Annahme, dass die Anwendung von einem bestimmten main Methode.

  • Wenn die Speichernutzung der kritische Punkt ist, können Sie Ihren Code so strukturieren, dass er dynamisches Laden verwendet, um varianten-, plattform- oder sogar anwendungsfallspezifische Klassen zu laden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X