Schnittstelle-Code vom Programm Trennen

disconnectus

Aktives Mitglied
Hallo zusammen,



ich habe eine Frage bezüglich der Trennung von Schnittstelle- und Programmcode. Das ist mein erstes großes Programm und ich wusste nicht, dass man das Programm auch ohne Benutzerschnittstelle (user gui) ausführbar machen musste, deswegen bekomme ich sehr viele Variablen nur über die Schnittstelle.

Ich möchte aber jetzt die Schnittstelle- und Programmcode voneinander trennen. Wie soll ich vorgehen?
Kann ich ein Boolean definieren und wenn das Programm über die Schnittstelle gestartet wird, setze ich diese Boolean auf wahr gesetzt. Danach im Programmcode frage ich ab ob diese Boolean „True“ ist. Wenn es „True“ ist, benutze ich die über der Benutzerschnittstelle gegebene Werte. Wenn es aber falsch ist, zuweise ich Werte zu der Variablen.

Ich möchte wie folgt Vorgehen:
Code:
    if(interfaceBoolean) {
       programparameter = interfaceparameter;        
     } else {       
       programparameter = 5;
     }


Ist das in Ordnung so? Was wäre besser?



Danke im Voraus
 
Ja klar kannst du es so machen, aber warum setzt du im else Zweig eine 5?
Lass das doch leer.
Du kannst ja später deine boolean-Variable wieder verwenden, um zu überprüfen, welche Zweige von deinem Code ausgeführt werden.

Wusstest du eigentlich schon, dass du auch Parameter an eine Java-Datei übergeben kannst ?
Die übergebenen Parameter kannst du dann in der Main-Methode abfangen:

Java:
    public static void main(String[] args) {
        if (args[0].equals("INTERFACE")) {
                 programparameter = args[1];
        }
        // Sonstige Verarbeitung
    }

So würde ich es vielleicht machen ...
 
Das ist mein erstes großes Programm und ich wusste nicht, dass man das Programm auch ohne Benutzerschnittstelle (user gui) ausführbar machen musste, deswegen bekomme ich sehr viele Variablen nur über die Schnittstelle.

Grundsätzlich muss er nicht, gibt dafür ja kein Gesetz.
Aber natürlich ist es sauberer GUI und Logik zu trennen. Der Vorteil davon wäre das man eine neue GUI auf einer anderen Technik basierend entwickeln kann ohne die Logik anpassen zu müssen.
Beispiel du hattest bis jetzt eine Swing GUI und willst auf JavaFX umsteigen. Bei einer sauberen Trennung musst du wirklich nur das UI anpassen, nicht die Logik.
Oder du willst das Programm über ein UI und auch eine Konsole bedienbar machen.

Kann ich ein Boolean definieren und wenn das Programm über die Schnittstelle gestartet wird, setze ich diese Boolean auf wahr gesetzt.
Danach im Programmcode frage ich ab ob diese Boolean „True“ ist. Wenn es „True“ ist, benutze ich die über der Benutzerschnittstelle gegebene Werte. Wenn es aber falsch ist, zuweise ich Werte zu der Variablen.

Dadurch erreichst du keine Trennung von UI und Logik, sondern nur eine Unterscheidung woher die Daten für das Programm genommen werden soll.
Die richtige Richtung wäre gegen Interfaces/Schnittstellen zu programmieren und das 3 Schichtenmodell anzuwenden.

Wusstest du eigentlich schon, dass du auch Parameter an eine Java-Datei übergeben kannst ?
Zum Glück hast du ein Beispiel geliefert, sonst hätte ich nicht gewusst was du mit Parameter an Java-Datei meinst.
-> man muss auf die Wortwahl aufpassen, das sind Parameter (oder auch Programmparameter, Startparameter) für die main-Methode (und nicht der Datei) 🙂
 
Ich habe Probleme, die eigentliche Anfrage zu verstehen. Das liegt evtl. an der Wortwahl bei der Fragestellung.

Wenn man etwas entwickelt, dann macht es sehr viel Sinn, den "Business-Code" von der "GUI" zu trennen. Das bedeutet, dass man den Code, der Daten vorhält und irgendwas macht von dem Code trennt, der irgendwas anzeigt. Gründe dazu gibt es sehr viele:
a) Mit der wichtigste Grund ist in meinen Augen die Testbarkeit. Alles, was keine GUI / Userinteraktion enthält, lässt sich sehr gut in Unit-Tests testen. Sobald es Außen-Schnittstellen gibt, dann braucht man meist etwas mehr Erfahrung und sogenannte Mocking Tools (Da wird dann - grob beschrieben - die Außen-Stelle simuliert) Aber wichtig ist: Automatische Tests, die der Entwickler ohne viel Zeitbedarf einfach so aufrufen kann (und die dann hoffentlich 100% des Codes testen).
b) Wiederverwendbarkeit - dies ist ein sehr wichtiger Punkt in meinen Augen. So kann ich schnell eine weitere Schnittstelle bereit stellen. Statt einer Swing Applikation kann es dann eine Webseite sein oder eine JavaFX Applikation oder oder oder ...

Dann ist ein Wort aufgetaucht: Interfaces. Interfaces können sehr hilfreich sein, um universell zu bleiben. So kann ich ein Interface KFZ beschreiben. Das enthält, wie ein KFZ gefahren werden kann. Jeder, der nun ein KFZ fahren kann, kann nun eine beliebige Instanz bekommen und damit dann fahren (so die Klasse der Instanz auch das Interface KFZ implementiert). Dabei ist es dann egal, ob es sich bei dem KFZ um ein PKW handelt oder ob es nur eine Simulation ist.
Konkret bedeutet dies, dass ich in einer Klasse z.B. eine HashMap nutze, um Schlüsseln gewisse Werte zuzuordnen. Aber nach Außen ist nur wichtig: Ich habe da eine Map. Wie diese genau implementiert ist, ist vollkommen egal. Das mag eine HashMap sein aber es kann prinzipiell jegliche Klasse eingesetzt werden, die eine Map implementiert. Dadurch kann ich intern in der Klasse die Datenhaltung ändern ohne dass es nach außen zu Problemen führt - und das selbst dann, wenn ich die Map komplett von außen zugreifbar gemacht habe - natürlich über das Interface Map und nicht über die Klasse HashMap!

Das geht dann evtl. auch bei Deinem Applikations-Code. Evtl. hast Du gewisse Teile, die ersetzbar sind. So gibt es ein Pattern Namens "Dependency Injection". Dahinter versteckt sich, dass meine Klasse irgend eine Abhängigkeit hat. Nehmen wir einfach einmal an, Du hättest eine Adressverwaltung und daher eine Klasse, die alle Adressen liest und Dir dann eine List<Adresse> liefert. Nun nutzt Deine Applikation eben diese Klasse und liest nun immer eine Datei ein mit den Adressen und speichert die bei Änderungen auch. Dependency Injection könnte hier jetzt bedeuten:
- Deine Applikation liest ein Config-File, welches den Namen einer Klasse enthält, mit der die Adressen geladen werden sollen.
- Diese genannte Klasse muss ein bestimmtes Interface implementieren. So muss z.B. ein "List <Adresse> getAllAddresses()" implementiert sein.
- Nun kann deine Applikation aus dem Config-File den Klassennamen ermitteln und eine Instanz der Klasse erzeugen um dann über dieses Interface zuzugreifen.
Der Vorteil ist, dass ich eine eigene Klasse schreiben kann, die dieses Interface implementiert und die Adressen z.B. aus einer Datenbank holt. Dann lege ich die Klasse mit in den Classpath der Applikation und ändere das Konfig-File so dass meine Klasse geladen wird.

War das etwas verständlich um zu sehen, was es so für Themen diesbezüglich gibt? Hilft es evtl., die Anforderungen/Fragen genauer zu formulieren?
 
Vielen Dank für die ausführlichen Antworten. Ich meinte eigentlich keinen Parameter. Ich wollte irgendeinen Variable-Name schreiben.

Soweit ich verstehe, einfachste weg ist, die über das Userinterface gelesene Variablen an meine Main-Methode (Main Methode des Logik-Teils) als Parameter zu übergeben und gleichzeitig die Booleans zu setzen.

Danach im Logik-Teil überprüfe ich diese Booleans, um zu entscheiden welche Zweige ausgeführt werden sollen.


Später, wenn ich wieder Zeit habe, werde ich wieder versuchen diese Trennung zu verbessern anhand eure Hilfestellungen. Ich muss irgendwann mal anfangen mit Patterns zu arbeiten. Wenn ich genug Zeit finde, möchte ich das „Dependency Injection“-Pattern lernen.
 
Dependency-Injection ist gut und Hilfreich bei großen Projekten; das ist also kein Fehler 🙂 Aber bezüglich deiner Programmparameter bringt es dich nicht weit.

Um mal ein etwas konkreteres Beispiel zu Bringen: Ich hab vor ein Programm zu schreiben, welches die Fakultät einer Zahl berechnet und dem Benutzer mitteilt. Ein unsauberes Konsolenprogramm würde so aussehen:
Java:
class FakultaetBerechner {
    public void main(String[] args) {
        try
        {
            int n = Integer.valueOf(args[0]);

            if (n <= 0)
            {
                System.err.println("Die Zahl muss größer als 0 sein.");
                return;
            }

            int resultat = n;

            if (n != 1)
                for (;n > 1;)
                    resultat *= --n;

            System.out.println(String.valueOf(resultat));
        }
        catch (NumberFormatException ex) { System.err.println("Eingabe muss eine Zahl sein."); return; }
        catch (ArrayIndexOutOfBoundsException ex) { System.err.println("Bitte eine Zahl als Parameter angeben."); return; }
    }
}

Ein unsauberes GUI-Programm würde eben ein JFrame erstellen, dort ein JTextField anzeigen und beim druck auf einen JButton im Listener die Fakuötät berechnen und als JDialog oder in einem JLabel wieder ausgeben.

Ein sauber getrenntes Programm würde die Schnittstellenlogik (Eingaben Abfragen und Ergebnisse anzeigen) von der Programmlogik (Fakultät berechnen) abkoppeln:
Java:
abstract class ExtendedMath {
    public static int fakultaet(int n) {
        if (n <= 0)
            throw new IllegalArgumentException("Parameter n must be greater than 0");

        if (n == 1)
            return n;

        int result = n;
        for (;n > 1;)
            result *= --n;
        
        return result;
    }
}
class FakultaetBerechner {
    public void main(String[] args) {
        try
        {
            int n = Integer.valueOf(args[0]);
            System.out.println(String.valueOf(ExtendedMath.fakultaet(n)));
        }
        catch (NumberFormatException ex) { System.err.println("Eingabe muss eine Zahl sein."); return; }
        catch (ArrayIndexOutOfBoundsException ex) { System.err.println("Bitte eine Zahl als Parameter angeben."); return; }
        catch (IllegalArgumentException ex) { System.err.println("Eingabe muss größer als 0 sein."); return; }
    }
}

Wenn sich jetzt an der Shcnittstelle etwas ändert, muss man wirklich nur die Schnittstelle anpassen, der Rest muss nicht einmal neu kompiliert werden. Gleiches gilt für die Berechnung der Fakultät (Die übrigens in diesem Beispiel hier wahrscheinlich nicht ganz korrekt ist).
Dieses Konzept kannst du jetzt "skalieren": Anstatt einzelne Methoden auszulagern machst du das mit ganzen Paketen von Klassen. Das Ziel ist es, dass du Code hast der ohne jegliche Zuhilfenahme von (G)UI Code deine Logik ausführen kann (was auch immer das ist). Dann kann man eine UI programmieren welche diese Klassen Zuhilfe nimmt und im wahrsten Sinne des Wortes "User Interface" dem Benutzer eine Schnittstelle zum Programmcode bietet.
 

Zurück
Oben