Part 11

Pakete (Packages)

Mit der zunehmenden Anzahl an Klassen im Programm wird es schwieriger, sich alle Funktionen und Methoden zu merken. Eine sinnvolle Benennung der Klassen und eine sorgfältige Planung, sodass jede Klasse eine klare Verantwortlichkeit hat, sind hilfreich. Zusätzlich ist es sinnvoll, die Klassen in Pakete zu unterteilen. Klassen in einem Paket können Funktionalitäten, Zwecke oder andere logische Eigenschaften teilen.

Pakete sind praktisch Verzeichnisse, in denen die Quellcodedateien organisiert werden.

IDEs bieten bestehende Werkzeuge zur Paketverwaltung. Bisher haben wir nur Klassen und Schnittstellen im Standardpaket des Ordners „Source Packages“ des Projekts erstellt. Sie können ein neues Paket in IntelliJ via File -> New -> Package erstellen. Die einzelnen Aufgaben der wöchentlichen Übung sind in Paketen organisiert.

Sie können Klassen innerhalb eines Pakets auf die gleiche Weise erstellen, wie im Standardpaket. Im Folgenden erstellen wir die Klasse Program im neu erstellten Paket library.

Das Paket einer Klasse (das Paket, in dem die Klasse gespeichert ist) wird zu Beginn der Quellcodedatei mit der Anweisung package *name-of-package*; angegeben. Im folgenden Beispiel befindet sich die Klasse Program im Paket library.

package library;

public class Program {

    public static void main(String[] args) {
        System.out.println("Hello packageworld!");
    }
}

Jedes Paket, einschließlich des Standardpakets, kann weitere Pakete enthalten. Zum Beispiel ist im Paket package library.domain das Paket domain im Paket library enthalten. Das Wort domain wird oft verwendet, um den Speicherplatz für Klassen zu bezeichnen, die Konzepte der Problemstellung repräsentieren. Beispielsweise könnte die Klasse Book im Paket library.domain gespeichert sein, da sie ein Konzept der Bibliotheksanwendung darstellt.

package library.domain;

public class Book {
    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

Eine Klasse kann auf Klassen in einem Paket zugreifen, indem sie die import-Anweisung verwendet. Die Klasse Book im Paket library.domain wird mit der Anweisung import library.domain.Book; zur Nutzung verfügbar gemacht. Die Import-Anweisungen, die zum Importieren von Klassen verwendet werden, werden in der Quellcodedatei nach der Paketdefinition platziert.

package library;

import library.domain.Book;

public class Program {

    public static void main(String[] args) {
        Book book = new Book("the ABCs of packages!");
        System.out.println("Hello packageworld: " + book.getName());
    }
}
Beispielausgabe

Hello packageworld: the ABCs of packages!

Ab diesem Punkt werden fast alle Übungen Pakete verwenden. Beginnen wir damit, unsere ersten eigenen Pakete zu erstellen.

Programmierübung:

FirstPackages (Part11_08)

Erste Pakete (3 Teile)

Benutzungsschnittstelle

Es gibt ein Paket namens de.techfak.Part11_08_FirstPackages, das in die Übung aufgenommen wurde. Wir werden die Funktionalität des Programms innerhalb dieses Pakets erstellen. Fügen Sie das Paket ui innerhalb des Pakets de.techfak.Part11_08_FirstPackages hinzu (woraufhin das Paket de.techfak.Part11_08_FirstPackages.ui verfügbar sein sollte), und fügen Sie eine Schnittstelle namens UserInterface hinzu.

Die Schnittstelle UserInterface definiert die Methode void update().

Textbenutzungschnittstelle

Erstellen Sie im selben Paket die Klasse TextInterface, die die Schnittstelle UserInterface implementiert. Implementieren Sie die Methode public void update(), die von der Schnittstelle UserInterface gefordert wird, so, dass sie nur den String "Updating UI" ausgibt, indem sie die Methode System.out.println aufruft.

Anwendungslogik

Erstellen Sie dann das Paket de.techfak.Part11_08_FirstPackages.logic. Erstellen Sie darin die Klasse ApplicationLogic. Die Funktionalität, die die Anwendungslogik bietet, sollte wie folgt aussehen:

  • public ApplicationLogic(UserInterface ui)
    Der Konstruktor der Klasse ApplicationLogic. Er erhält als Parameter eine Klasse, die die Schnittstelle UserInterface implementiert. NB: Damit die Anwendungslogik die Schnittstelle sehen kann, muss sie "importiert" werden. Fügen Sie die Zeile import de.techfak.Part11_08_FirstPackages.ui.UserInterface am Anfang der Datei hinzu.
  • public void execute(int times)
    Gibt die Zeichenkette "Application logic is working" so oft aus, wie es durch die Variable times angegeben ist. Nach jeder Ausgabe sollte die Methode die Methode update() des Objekts aufrufen, das als Konstruktorparameter übergeben wurde (das die Schnittstelle UserInterface implementiert).

Sie können das Programm mit der folgenden Main-Program-Klasse testen.

import de.techfak.Part11_08_FirstPackages.logic.ApplicationLogic;
import de.techfak.Part11_08_FirstPackages.ui.UserInterface;
import de.techfak.Part11_08_FirstPackages.ui.TextInterface;

public class Main {

    public static void main(String[] args) {
        UserInterface ui = new TextInterface();
        new ApplicationLogic(ui).execute(3);
    }
}
Beispielausgabe

Application logic is working Updating UI Application logic is working Updating UI Application logic is working Updating UI


Sie müssen hierfür den Artemis Server verwenden und die entsprechende Aufgabe lösen.

Hier klicken.

Programmierübung:

TheThreePackages (Part11_09)

Drei Pakete

Erstellen Sie innerhalb der Übungsbasis drei Pakete: de.techfak.Part11_09_TheThreePackages.a, de.techfak.Part11_09_TheThreePackages.b und de.techfak.Part11_09_TheThreePackages.c. Erstellen Sie die Klasse A im Paket ...a, die Klasse B im Paket ...b und die Klasse C im Paket ...c. Die Klassen benötigen keine Objektvariablen, Konstruktoren oder Methoden. Beachten Sie, dass der unschöne Paketname durch die Übungsaufgabe erzwungen wird.


Sie müssen hierfür den Artemis Server verwenden und die entsprechende Aufgabe lösen.

Hier klicken.

Verzeichnisstruktur in einem Dateisystem

Jedes Projekt, das Sie in IntelliJ sehen, befindet sich im Dateisystem Ihres Computers oder auf einem zentralen Server.

Das Projektverzeichnis src/main/java enthält die Quellcodedateien des Programms. Befindet sich das Paket einer Klasse im library-Paket, wird diese Klasse im src/main/java/libary-Ordner des Quellcodedateiverzeichnisses gespeichert. Sie können auch die konkrete Projektstruktur in IntelliJ via View -> Windows -> Project überprüfen.

Pakete und Zugriffsmodifikatoren

Bisher haben wir zwei Zugriffsmodifikatoren verwendet. Der Modifikator private wird verwendet, um Variablen (und Methoden) zu definieren, die nur innerhalb der Klasse sichtbar sind, in der sie definiert sind. Sie können von außerhalb dieser Klasse nicht verwendet werden. Die mit public definierten Methoden und Variablen hingegen sind für jede und jeden nutzbar.

package library.ui;

public class UserInterface {
    private Scanner scanner;

    public UserInterface(Scanner scanner) {
        this.scanner = scanner;
    }

    public void start() {
        printTitle();

        // other functionality
    }

    private void printTitle() {
        System.out.println("***********");
        System.out.println("* LIBRARY *");
        System.out.println("***********");
    }
}

Wenn Sie ein Objekt der Klasse UserInterface oben erstellen, sind der Konstruktor und die start-Methode von überall im Programm aufrufbar. Die Methode printTitle und die Variable scanner sind jedoch nur innerhalb der Klasse verfügbar.

Fehlt der Zugriffsmodifikator, sind die Methoden und Variablen nur innerhalb desselben Pakets sichtbar. Dies nennen wir den Standard- oder Paketmodifikator. Ändern wir das obige Beispiel so, dass die Methode printTitle den Paketzugriffsmodifikator hat.

package library.ui;

public class UserInterface {
    private Scanner scanner;

    public UserInterface(Scanner scanner) {
        this.scanner = scanner;
    }

    public void start() {
        printTitle();

        // other functionality
    }

    void printTitle() {
        System.out.println("***********");
        System.out.println("* LIBRARY *");
        System.out.println("***********");
    }
}

Nun können die Klassen innerhalb desselben Pakets — also die Klassen im Paket library.ui — die Methode printTitle verwenden.

package library.ui;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        UserInterface ui = new UserInterface(scanner

);

        ui.printTitle(); // funktioniert!
    }
}

Befindet sich eine Klasse in einem anderen Paket, kann die Methode printTitle nicht aufgerufen werden. Im folgenden Beispiel befindet sich die Klasse Main im Paket library. Da die Methode printTitle im Paket library.ui ist und den Paketzugriffsmodifikator hat, kann sie nicht verwendet werden.

package library;

import java.util.Scanner;
import library.ui.UserInterface;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        UserInterface ui = new UserInterface(scanner);

        ui.printTitle(); // funktioniert nicht!
    }
}

Ein größeres Beispiel: Flugkontrolle

Schauen wir uns ein Programm an, das eine Textbenutzungschnittstelle zum Hinzufügen und Überprüfen von Flugzeugen und Flügen bietet. Die Benutzungsschnittstelle des Programms sieht folgendermaßen aus.

Beispielausgabe

Airport Asset Control --------------------

Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 1 Give the airplane id: HA-LOL Give the airplane capacity: 42 Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 1 Give the airplane id: G-OWAC Give the airplane capacity: 101 Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 2 Give the airplane id: HA-LOL Give the departure airport id: HEL Give the target airport id: BAL Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 2 Give the airplane id: G-OWAC Give the departure airport id: JFK Give the target airport id: BAL Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 2 Give the airplane id: HA-LOL Give the departure airport id: BAL Give the target airport id: HEL Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > x

Flight Control ------------

Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > 1 G-OWAC (101 capacity) HA-LOL (42 capacity) Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > 2 HA-LOL (42 passengers) (HEL-BAL) HA-LOL (42 passengers) (BAL-HEL) G-OWAC (101 passengers) (JFK-BAL)

Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > 3 Give the airplane id: G-OWAC G-OWAC (101 capacity)

Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > x

Es gibt viele "Konzepte", die für diese Problemstellung relevant sind, wesentliche sind durch die Klassen Airplane und Flight repräsentiert. Jeder Flug beinhaltet auch einen Place (Abflugs- und Zielorte). Zusätzlich zu den Konzepten, die die Problemstellung repräsentieren, enthält das Programm auch eine Textbenutzungschnittstelle und eine Klasse, über die die Textbenutzungschnittstelle die Konzepte verwendet.

Die Paketstruktur des Programms könnte folgendermaßen aussehen (zum Beispiel):

  • flightControl - enthält die Hauptprogramms-Klasse (die Klasse, die bspw. main enthält), die benötigt wird, um das Programm zu starten.

  • flightControl.domain - enthält die Klassen, die Konzepte der Problemstellung repräsentieren: Airplane, Flight und Place.

  • flightControl.logic - enthält die Funktionalität, die zur Steuerung der Anwendung verwendet wird.

  • flightControl.ui - enthält die Textbenutzungschnittstelle.

Im nächsten Abschnitt beschreiben wir eine mögliche Organisation des Programms (ohne die Hauptprogramms-Klasse).

Klassen, die Konzepte der Problemstellung repräsentieren

Die Klassen, die Konzepte der Problemstellung repräsentieren, werden oft in einem Paket namens domain abgelegt. Da sich die gesamte Anwendung im Paket flightControl befindet, platzieren wir das Paket domain innerhalb des Pakets flightControl. Konzepte der Problemstellung werden durch die Klassen Place, Airplane und Flight repräsentiert.

package flightControl.domain;

public class Place {

    private String ID;

    public Place(String ID) {
        this.ID = ID;
    }

    @Override
    public String toString() {
        return this.ID;
    }
}
package flightControl.domain;

public class Airplane {

    private String id;
    private int capacity;

    public Airplane(String id, int capacity) {
        this.id = id;
        this.capacity = capacity;
    }

    public String getID() {
        return this.id;
    }

    public int getCapacity() {
        return this.capacity;
    }

    @Override
    public String toString() {
        return this.id + " (" + this.capacity + " capacity)";
    }
}
package flightControl.domain;

public class Flight {

    private Airplane airplane;
    private Place departureAirport;
    private Place targetAirport;

    public Flight(Airplane airplane, Place departureAirport, Place targetAirport) {
        this.airplane = airplane;
        this.departureAirport = departureAirport;
        this.targetAirport = targetAirport;
    }

    public Airplane getAirplane() {
        return this.airplane;
    }

    public Place getDeparturePlace() {
        return this.departureAirport;
    }

    public Place getTargetPlace() {
        return this.targetAirport;
    }

    @Override
    public String toString() {
        return this.airplane.toString() + " (" + this.departureAirport + "-" + this.targetAirport + ")";
    }
}

Anwendungslogik

Die Anwendungslogik wird in der Regel von den Klassen getrennt, die Konzepte der Problemstellung darstellen. In unserem Beispiel wird die Anwendungslogik im Paket logic gespeichert. Die Anwendungslogik umfasst die Funktionalität zum Hinzufügen von Flugzeugen und Flügen sowie deren Auflistung.

package flightControl.logic;

import java.util.Collection;
import flightControl.domain.Flight;
import flightControl.domain.Airplane;
import java.util.HashMap;
import java.util.Map;
import flightControl.domain.Place;

public class FlightControl {

    private HashMap<String, Airplane> airplanes = new HashMap<>();
    private HashMap<String, Flight> flights = new HashMap<>();
    private Map<String, Place> places;

    public FlightControl() {
        this.flights = new HashMap<>();
        this.airplanes = new HashMap<>();
        this.places = new HashMap<>();
    }

    public void addAirplane(String ID, int capacity) {
        Airplane plane = new Airplane(ID, capacity);
        this.airplanes.put(ID, plane);
    }

    public void addFlight(Airplane plane, String departureID, String destinationID) {
        this.places.putIfAbsent(departureID, new Place(departureID));
        this.places.putIfAbsent(destinationID, new Place(destinationID));

        Flight flight = new Flight(plane, this.places.get(departureID), this.places.get(destinationID));
        this.flights.put(flight.toString(), flight);
    }

    public Collection<Airplane> getAirplanes() {
        return this.airplanes.values();
    }

    public Collection<Flight> getFlights() {
        return this.flights.values();
    }

    public Airplane getAirplane(String ID) {
        return this.airplanes.get(ID);
    }
}

Textbenutzungschnittstelle

Die Benutzungsschnittstelle ist von der Anwendungslogik und den Klassen, die die Problemstellung repräsentieren, getrennt. In diesem Beispiel wird die Benutzungsschnittstelle im Paket ui gespeichert.

package flightControl.ui;

import flightControl.domain.Flight;
import flightControl.domain.Airplane;
import java.util.Scanner;
import flightControl.logic.FlightControl;

public class TextUI {
    private FlightControl flightControl;
    private Scanner scanner;

    public TextUI(FlightControl flightControl, Scanner scanner) {
        this.flightControl = flightControl;
        this.scanner = scanner;
    }

    public void start() {
        // let's start in two parts -- first start the asset control,
        // then the flight control
        startAssetControl();
        System.out.println();
        startFlightControl();
        System.out.println();
    }

    private void startAssetControl() {
        System.out.println("Airport Asset Control");
        System.out.println("--------------------");
        System.out.println();

        while (true) {
            System.out.println("Choose an action:");
            System.out.println("[1] Add an airplane");
            System.out.println("[2] Add a flight");
            System.out.println("[x] Exit Airport Asset Control");

            System.out.print("> ");
            String answer = scanner.nextLine();

            if (answer.equals("1")) {
                addAirplane();
            } else if (answer.equals("2")) {
                addFlight();
            } else if (answer.equals("x")) {
                break;
            }
        }
    }

    private void addAirplane() {
        System.out.print("Give the airplane id: ");
        String id = scanner.nextLine();
        System.out.print("Give the airplane capacity: ");
        int capacity = Integer.parseInt(scanner.nextLine());

        this.flightControl.addAirplane(id, capacity);
    }

    private void addFlight() {
        System.out.print("Give the airplane id: ");
        Airplane airplane = askForAirplane();
        System.out.print("Give the departure airport id: ");
        String departureID = scanner.nextLine();
        System.out.print("Give the target airport id: ");
        String destinationID = scanner.nextLine();

        this.flightControl.addFlight(airplane, departureID, destinationID);
    }

    private void startFlightControl() {
        System.out.println("Flight Control");
        System.out.println("------------");
        System.out.println();

        while (true) {
            System.out.println("Choose an action:");
            System.out.println("[1] Print airplanes");
            System.out.println("[2] Print flights");
            System.out.println("[3] Print airplane details");
            System.out.println("[x] Quit");

            System.out.print("> ");
            String answer = scanner.nextLine();
            if (answer.equals("1")) {
                printAirplanes();
            } else if (answer.equals("2")) {
                printFlights();
            } else if (answer.equals("3")) {
                printAirplaneDetails();
            } else if (answer.equals("x")) {
                break;
            }
        }
    }

    private void printAirplanes() {
        for (Airplane plane : flightControl.getAirplanes()) {
            System.out.println(plane);
        }
    }

    private void printFlights() {
        for (Flight flight : flightControl.getFlights()) {
            System.out.println(flight);
            System.out.println("");
        }
    }

    private void printAirplaneDetails() {
        System.out.print("Give the airplane id: ");
        Airplane plane = askForAirplane();
        System.out.println(plane);
        System.out.println();
    }

    private Airplane askForAirplane() {
        Airplane airplane = null;
        while (airplane == null) {
            String id = scanner.nextLine();
            airplane = flightControl.getAirplane(id);

            if (airplane == null) {
                System.out.println("No airplane with the id " + id + ".");
            }
        }

        return airplane;
    }
}
Programmierübung:

FlightControl (Part11_10)

Flugkontrolle (2 Teile)

In dieser Übung werden Sie die Anwendung implementieren, die oben beschrieben wurde. Sie sind frei, die Struktur nach Belieben zu gestalten, oder Sie können der oben skizzierten Struktur folgen (natürlich müssen Sie die Paketnamen anpassen). Das Erscheinungsbild der Benutzungsschnittstelle und die erforderlichen Befehle sind vorgegeben.

NB: Damit die Tests funktionieren, dürfen Sie in Ihrem Programm nur ein Scanner-Objekt erstellen, um Benutzereingaben zu lesen.

In dieser Übung implementieren Sie eine Flugkontrollanwendung. Sie wird verwendet, um die Flugzeuge und ihre Flugrouten zu steuern. Das System kennt immer die Kennung und die Kapazität eines Flugzeugs. Die Fluginformationen bestehen aus dem verwendeten Flugzeug, der Abflug-Flughafen-ID (z. B. HEL) und der Ziel-Flughafen-ID (z. B. BAL).

Es kann mehrere Flugzeuge und Flüge geben. Dasselbe Flugzeug kann für mehrere Flüge verwendet werden.

Die Anwendung sollte in zwei Teilen funktionieren: Zunächst gibt der Benutzer Informationen über Flugzeuge und Flüge in der Flughafen-Vermögenskontrolle ein, danach bietet das Programm den Fluginformationsdienst für den Benutzer an. In dieser letzteren Flugkontrolle gibt es drei Operationen: die Flugzeuge drucken, die Flüge drucken und die Informationen eines einzelnen Flugzeugs drucken. Zusätzlich kann der Benutzer das Programm beenden, indem er die Option x wählt. Wenn der Benutzer einen ungültigen Befehl eingibt, fragt das Programm erneut nach einem Befehl.

Alle von Ihnen implementierten Pakete müssen als Präfix de.techfak.Part11_10_FlightControl verwenden. Das Programm sollte gestartet werden, wenn die Main-Methode der Klasse Main im Paket de.techfak.Part11_10_FlightControl ausgeführt wird

Ein Beispielausgabe des Programms wird unten dargestellt:

Beispielausgabe

Airport Asset Control -------------------- Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 1 Give the airplane id: HA-LOL Give the airplane capacity: 42 Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 1 Give the airplane id: G-OWAC Give the airplane capacity: 101 Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 2 Give the airplane id: HA-LOL Give the departure airport id: HEL Give the target airport id: BAL Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 2 Give the airplane id: G-OWAC Give the departure airport id: JFK Give the target airport id: BAL Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > 2 Give the airplane id: HA-LOL Give the departure airport id: BAL Give the target airport id: HEL Choose an action: [1] Add an airplane [2] Add a flight [x] Exit Airport Asset Control > x

Flight Control ------------ Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > 1 G-OWAC (101 capacity) HA-LOL (42 capacity) Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > 2 HA-LOL (42 capacity) (HEL-BAL) HA-LOL (42 capacity) (BAL-HEL) G-OWAC (101 capacity) (JFK-BAL)

Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > 3 Give the airplane id: G-OWAC G-OWAC (101 capacity)

Choose an action: [1] Print airplanes [2] Print flights [3] Print airplane details [x] Quit > x

NB Für die Zwecke des Tests ist es wichtig, dass die Benutzungsschnittstelle genau wie oben beschrieben funktioniert. Sie sollten wahrscheinlich die vom Programm gedruckten Optionen von hier in Ihren Code kopieren. Die Tests werden nicht davon ausgehen, dass Ihr Programm auf unsachgemäße Eingaben vorbereitet ist.


Sie müssen hierfür den Artemis Server verwenden und die entsprechende Aufgabe lösen.

Hier klicken.

Sie haben das Ende dieses Abschnitts erreicht! Weiter zum nächsten Abschnitt: