Erste Schritte Sinvolle Architektur und paketübergreifendes Arbeiten

Dibelius

Mitglied
Hallo,
zwar bin ich kein Java-Anfänger, komme aber bei meinen bisherigen Projekten immer wieder an einen Punkt, den ich offenbar noch nicht verinnerlicht habe. Dazu ein Auszug aus dem Leitfaden der 12 häufigsten Anfänger-Fehler:
4. Nicht ausreichende Befassung mit Software-Entwurf und der Architektur
Das Sprachgefühl und die Syntax sind der erste Schritt. Inhaltliches Wissen rund um Methoden, Klassen und der gelieferten Bibliothek sind der zweite.
Die große Kunst bei der Software-Entwicklung ist aber nicht nur eine saubere Implementierung von Methoden. Viel schwieriger ist ein geeigneter Software-Entwurf, der auch später anpassbar ist, übersichtlich strukturiert und auf einer guten Architektur aufbaut. Es ist also wichtig, sich zusätzlich mit der Software-Architektur zu befassen. Das beinhaltet unter anderem auch Aspekte wie Design Patterns.

Das klingt erstmal simpel und könnte man schnell abhaken, nach dem Motto "ja, ist schon klar".

Nachdem ich nun aber mein aktuelles Projekt schon mehrfach umgeworfen und neu strukturiert habe, bemerke ich immer wieder eines: ich arbeite ausschließlich in einem einzigen Paket, nahezu alle Klassen, Interfaces, Enums usw. sind paketsichtbar, bis auf die Klasse, über die die eigentliche Funktionalität aufgebaut wird (nicht die Hauptklasse des Projekts). Vom Ansatz her finde ich das logisch, aber ich würde gerne mehr aufteilen, einerseits zugunsten der Übersichtlichkeit, für die Wiederverwendbarkeit und ganz besonders für spätere Anpassungen, denn darüber stolpere ich immer wieder.

Meine grobe Planung sieht bisher so aus (für ein GUI-Programm). Die import-Anweisungen habe ich hier z.T. weggelassen.

Hauptklasse des Projekts, ausschließlich zu Testzwecken (wirklich nötig oder sinnvoll?)
Java:
// (default package)
// import ...
public class Test {
    public static void main(String[] args) {
        // Zum Testen von Sichtbarkeiten
        GUIMain app = myproject.GUIMain(null);
        //app.Behavior.TYPE_1; // public visibility
    }
}

Eigentlich Funktionsklasse, die das JFrame erzeugt
Java:
package myproject;
// import ...
public class GUIMain extends JFrame {
    final Core core; // Verwalter-Klasse
    private Behavior behavior;
    private DocType docType;
    public GUIMain(Behavior behavior) {
        super();
        this.behavior = (behavior != null) ? behavior : Behavior.TYPE_1; // lässt sich während der Laufzeit umstellen
        this.core = new Core(this);
        // weitere Initialisierungen, GUI aufbauen ...
    }
}

Global sichtbare Enumeration zum Steuern des Verhaltens
Java:
package myproject;

public enum Behavior {
    TYPE_1(32),
    TYPE_2(64);
    private Type(int v) {
        this.v = v;
        // weitere Initialisierungen ...
    }
    final private v;
    // ggf. weitere Funktionalität
}

Verwalter-Klasse
Java:
package myproject;

class Core {
    final GUIMain source;
    final SomeManager someManager;
    final AnotherManager anotherManager;
    // ...
    Core(GUIMain source) {
        this.source = source;
        this.someManager = new SomeManager(this);
        this.anotherManager = new AnotherManager(this); // ohne Code-Beispiel, analog zu SomeManager
        // ...
    }
    // Beispielmethode zur Erzeugung eines neuen Dokuments, wenn man im Menü von GUIMain auf "Datei/Neu" klickt
    void registerNewDocument() {
        if (this.someManager.hasDirectory()) {
            // Dokument im bestehenden Verzeichnis anlegen ...
        }
    }
    // weitere Verwaltungsmethoden ...
}

SomeManager (z.B. zum Einlesen und Aufbereiten von XML-Dateien)
Java:
package myproject;

class SomeManager extends AbstractManager {
    SomeManager(Core core) {
        super(core);
    }
    boolean hasDirectory() {
        return true; // vereinfacht, hier natürlich sinnvolle Überprüfung, statt true ...
    }
}

AbstractManager
Java:
package myproject;

abstract class AbstractManager {
    protected Core core;
    AbstractManager(Core core) {
        this.core = core;
    }
    // ggf. weitere gemeinsame Funktionalität
}

Nun erzeugt meine Klasse GUIMain weitere GUI-Elemente, die bspw. von JPanel oder JDialog abgeleitete Klassen verwenden. Alle zusammen würde ich gerne auslagern in ein separates Paket, wie myproject.gui, bspw. auch, um es später in anderen Projekten wiederverwenden zu können, als public Klassen. Problem dabei ist, dass sich fast alle Klassen bei mir untereinander kennen und Kreuz- und Querverweise existieren, die diesen Ansatz unmöglich machen. Zum Beispiel ist immer irgendwo die Verwalter-Klasse Core eingebunden, was mir auch notwendig erscheint.

Weiter habe ich momentan von außen nur Zugriff auf den Konstruktor von GUIMain und Behavior-Enum. Das ist auch so gewünscht. Alles weitere läuft intern im Paket ab, und soll, wie oben beschrieben, auf (Unter-)Pakete ausgweitet werden.

Das Paket-Konzept war mir bis vor kurzem noch nicht völlig klar, aber wenn ich es richtig verstehe und so, wie die Eclipse IDE es im Package-Baum darstellt, steht jedes Paket für sich und Unterpakete analog zur Verzeichnisstruktur existieren gar nicht? Wenn das so wäre, würde ich erwarten, dass ich von einem Unterpaket aus Zugriff auf paketsichtbare Klassen des übergeordneten Paketes habe.

Java:
package myproject.gui; // Unterpaket?
//package myotherproject; // Variante ohne Unterpaket?
import myproject;
public class GUISomeDialog extends JDialog
{
    private DocType docType;
    public GUISomeDialog() {
        super();
        this.docType = DocType.DOC_TYPE_1; // DocType ist nicht sichtbar!
    }
}
Java:
package myproject;
// nur paketsichtbar, weil so gewünscht
enum DocType {
    DOC_TYPE_1,
    DOC_TYPE_2,
    DOC_TYPE_3;
}

Sicher hinkt das Beispiel an der einen oder anderen Stelle, aber ich hoffe, es ist trotzdem verständlich.

Entspricht dieser Ansatz dem üblichen Vorgehen oder gibt es Verbesserungsbedarf?

Wie kann ich meine Klassen autarker gestalten, möglichst ohne dass sie sich untereinander kennen müssen? Also ohne die Source-Initialisierungen der Form new SomeClass(this);

Vielen Dank vorab!
Gerne auch Links zu guten Tutorials, die sich mit dem Thema befassen.
 

Bela B.

Aktives Mitglied
Bin am Handy unterwegs, daher nur kurz ein paar Dinge:

Ich verstehe ehrlich gesagt noch nicht so ganz, was bei dir "Behavior" macht, aber das hat (denke ich?) ja auch nicht direkt etwas mit deiner Frage zu tun.

Wichtig ist zumindest für eine wartbarere Struktur: Interfaces

Und damit einhergehend wie in deinem zitieren Ausschnitt auch erwähnt: Design Patterns

Für Design Patterns kann ich das Buch "Entwurfsmuster von Kopf bis Fuß" nur wärmstens empfehlen.

Bei den Patterns kommst du dann auch Mal bei dem allseits bekannten MVC an.

Das verringert dann richtig angewandt deine Kopplung zwischen den einzelnen Bestandteilen Model, View und Controller erheblich und grenzt die Zuständigkeiten ab.

Mit Swing habe ich keine Ahnung mehr, aber gerade bei JavaFX bietet sich statt MVC die Abwandlung MVVM mehr an.
 

mihe7

Top Contributor
Das klingt erstmal simpel und könnte man schnell abhaken, nach dem Motto "ja, ist schon klar".
Äh, das ist so ziemlich die Essenz der ganzen Softwareentwicklung. Die Programmiersprache ist dagegen fast egal.

Pakete sind zunächst einfach mal Namensräume zur Vermeidung von Namenskonflikten. In Java werden Pakete in der Regel auf Verzeichnisse abgebildet.

Das Top-Level-Paket identifiziert oft das Projekt. Wenn ich z. B. für das Forum ein Programm XYZ beue, könnte der Paketname org.javaforum.mihe7.xyz sein. In der nächsten Ebene (der ersten unterhalb des Projekts) sollten die Fachlichkeiten erkennbar sein. Sagen wir mal, XYZ hätte eine Adressverwaltung, dann gäbe es vermutlich ein Paket org.javaforum.mihe7.xyz.address.

Darunter kann man dann aufteilen, z. B. in Model, View, Controller (Reenskaug) oder Boundary, Control und Entity (Jacobson) oder z. B. auch Entity, Repository, Service (in Anlehnung an Evans Domain-Driven-Design) .

Picken wir mal beispielhaft BCE heraus, dann gäbe es ein boundary-Paket, in dem sich die UI-Klassen befänden. Im entity-Paket würde sich z. B. die Klasse finden, die die Adresse repräsentiert. Im control-Paket läge Fachlogik, die nicht direkt auf eine Entity abgebildet werden kann (im Original wird im control-Paket die Klasse für den Anwendungsfall untergebracht). Wenn Du die Pakete nach MVC aufteilst, hättest Du im Model die Adresse, im Controller z. B. Deine ActionListener und in der View die ... View-Klassen.

Achtung: es geht hier nicht um die Umsetzung der Patterns, lediglich um die Platzierung der Klassen in Anlehnung an das jeweilige Pattern. Es ist also problemlos möglich, das MVC-Pattern im Code umzusetzen, die Pakete nach dem BCE-Pattern aufzubauen. Der Sinn des ganzen ist einfach der, dass wenn ich z. B. Klassen bzgl. einer Adresse suche, sofort weiß, dass ich sie im entity-Paket (bzw. model-Paket) unterhalb des address-Pakets finde.

Viel wichtiger ist die Ebene darüber: wenn ich in das betreffende Verzeichnis des Projektpakets wechsle, will ich sehen können, um welche Anwendung es sich handelt. Steht dort address, invoice etc. weiß ich worum es geht. Steht dort dagegen ui, db, enums ist das völlig nichtssagend.

So viel zu den Paketen.

Für den eigentlichen Entwurf gibt es einige Ansätze. Domain-Driven-Design ist hier besonders zu nennen. Für die Anbindung des UIs hat man in der Regel irgendeine Form von Observer-Pattern, was dann meist die Bezeichnung MVC oder eine daran angelehnte erhält. Der Name spielt aber keine Rolle, wichtig ist die Trennung von UI und Logik.
 

Ähnliche Java Themen

Neue Themen


Oben