IO/UI

Aus Programmieren-Wiki
🚧 Diese Seite befindet sich in Bearbeitung 🚧
🤓 Diese Seite ist eine Bewertungsrichtlinie, die ab Blatt 2 annotiert und ab Blatt 3 abgezogen wird. 🤓

Beschreibung

Einleitung

Wir benutzen zu Debugging-Zwecken gerne System.out.println innerhalb von Codestellen. Diese Methode ist zwar ein einfacher Weg, schnell Code zu debuggen, allerdings ist das als eine finale Benutzerschnittstelle nicht geeignet. Generell gilt es Benutzerinteraktion nicht auf das Programm zu verteilen, sondern eine geeignete, zentralisierte Benutzerschnittstelle zu erstellen. Tut man das nicht, ergeben sich folgende gravierende Nachteile:

  • Schlechte Wartbarkeit: Es müssen an vielen Stellen Änderungen gemacht werden. Durch die Mischung werden diese Änderungen sehr verschwert
  • Schlechte Flexibilität: Schnelles ändern der Benutzerschnittstelle oder der Programmlogik können Seiteneffekte verursachen
  • Ablauf schwer zu verstehen: Ist der Fehler in der Schnittstelle, der Logik oder wurde eine falsche Eingabe getätigt?
  • Schlechte Testbarkeit: Es lässt sich schwer zwischen "debugging"- und "normalen-" Ausgaben unterscheiden beziehungsweise erzeugt eine gute Trennung viel Aufwand

Definition

Wir bezeichnen die Trennung innerhalb des Quellcodes zwischen Teilen, die für die Interaktion gedacht sind und Teilen, die für das eigentliche Programm bestimmt sind als Trennung der Benutzerinteraktion und Programmlogik. Im Falle von Java (und demnach des Übungsbetriebes) bedeutet das:

  • Klassen für Programmlogik:
    • Erstellen keine Ausgaben
    • Lesen keine Eingaben
    • Validiert Semantik der Eingaben
    • Besitzen öffentliche Schnittstelle von Methoden durch die von "außen" der Programmablauf gesteuert werden kann
  • Klassen für Benutzerinteraktion:
    • Liest Eingaben
    • Erstellt Ausgaben
    • Benutzt die öffentliche Schnittstelle um mit dem Programm zu kommunizieren
    • Validiert Syntax der Eingaben (z.B. Ist die Zahl ein Integer?)

Umsetzung

Zunächst wird gefordert, selbst IO und Programmlogik zu trennen. In späteren Blättern gibt es allerdings eine Vorlage für ein Command Pattern Hier eine Anleitung, dieses Prinzip leicht selbst umzusetzen:

  • public static void main(String[] args) kommt in einge eigene Klasse. Hier wird nur Benutzerinteraktion getätigt
  • Identifizieren der "obersten" Klasse. Beisplsweise bei einer Implementierung eines Brettspiels die Klasse für das Spielbrett, welche sich selbst mit Spielfiguren initialisiert.
  • Die Klasse benötigt eine ausreichende Schnittstelle für die Benutzerinteraktionsklasse (getter- und setter-Methoden zum Beispiel)
  • Alle Klassen die jetzt nicht an der Benutzerinteraktion beteiligt sind, dürfen hier jetzt keine Ein-/Ausgaben tätigen
  • Die Semantik wird jetzt von der Programmlogikklasse geprüft. Beispielsweise wird für die Erstellung eines Arrays wird ein positiver Wert benötigt
  • Die Syntax wird von der Benutzerinteraktionsklasse geprüft. Beispielsweise ist eine Zeichenkette als Geschwindigkeit eines Autos unzulässig


Negativbeispiel

public class UI {
	public static void main(String[] args) {
		Account a = new Account(100);
		while(true) {
			System.out.println("Input an Integer");
			String input = ""; 
			// We will imagine the input being produced here
			int amount = 0;
			try {
				amount = Integer.parseInt(input);
			} catch(NumberFormatException e) {
				continue;
			}
			a.transfer(amount);
		}
	}
}

public class Account {
	private int balance;
	
	public Account(int startingBalance) {
		balance = startingBalance;
	}
	
	public void transfer(int amount) {
		if (balance + amount > 0) {
			balance += amount;
			System.out.println("Balance: " + balance);
		} else {
			System.out.println("Unable to make transfer. Your balance is: " + balance);
		}
	}
}

Positivbeispiel

public class UI {
	public static void main(String[] args) {
		while(true) {
			System.out.println("Input an Integer");
			String input = "";
			// We again imagine the input is present now
			int amount = 0;
			try {
				amount = Integer.parseInt(amount);
			} catch(NumberFormatException e) {
				continue;
			}
		}
		
		if (a.transfer(amount)) {
			System.out.println("Balance: " + a.getBalance());
		} else {
			System.out.println("Unable to make transfer. Your balance is: " + a.getBalance())
		}
	}
}

public class Account {
	private int balance;
	
	public Account(int startingBalance) {
		this.balance = startingBalance;
	}
	
	public boolean transfer(int amount) {
		if (balance + amount > 0) {
			balance += amount;
			return true;
		} else {
			return false;
		}
	}
	
	public int getBalance() {
		return balance;
	}
}


Wenn du diese Seite interessant fandest, findest du hier noch mehr Seite(n) dazu:
Komplexität