Part 5

Objektorientierte Programmierung

Worum geht es bei der objektorientierten Programmierung?

Lassen Sie uns einen Moment innehalten und uns anschauen, wie eine Uhr funktioniert. Die Uhr hat drei Zeiger: Stunden, Minuten und Sekunden. Der Sekundenzeiger bewegt sich jede Sekunde einmal, der Minutenzeiger alle sechzig Sekunden und der Stundenzeiger alle sechzig Minuten. Wenn der Sekundenzeiger den Wert 60 erreicht, wird sein Wert auf null gesetzt, und der Minutenzeiger erhöht sich um eins. Wenn der Minutenzeiger den Wert 60 erreicht, wird sein Wert auf null gesetzt, und der Stundenzeiger erhöht sich um eins. Wenn der Stundenzeiger den Wert 24 erreicht, wird er auf null gesetzt.

Die Zeit wird immer im Format Stunden: Minuten: Sekunden ausgegeben, wobei zwei Ziffern verwendet werden, um die Stunde (z. B. 01 oder 12) sowie die Minuten und Sekunden darzustellen.

Die Uhr wurde unten mit Integer-Variablen implementiert (die Ausgabe könnte in einer separaten Methode erfolgen, was hier jedoch nicht der Fall ist).

int hours = 0;
int minutes = 0;
int seconds = 0;

while (true) {
    // 1. Printing the time
    if (hours < 10) {
        System.out.print("0");
    }
    System.out.print(hours);

    System.out.print(":");

    if (minutes < 10) {
        System.out.print("0");
    }
    System.out.print(minutes);

    System.out.print(":");

    if (seconds < 10) {
        System.out.print("0");
    }
    System.out.print(seconds);
    System.out.println();

    // 2. The second hand's progress
    seconds = seconds + 1;

    // 3. The other hand's progress when necessary
    if (seconds > 59) {
        minutes = minutes + 1;
        seconds = 0;

        if (minutes > 59) {
            hours = hours + 1;
            minutes = 0;

            if (hours > 23) {
                hours = 0;
            }
        }
    }
}
Wie Sie dem obigen Beispiel entnehmen können, ist es für jemanden, der den Code liest, nicht sofort ersichtlich, wie eine Uhr, die aus drei int-Variablen besteht, funktioniert. Es ist schwierig, anhand des Codes zu „sehen“, was vor sich geht. Ein berühmter Programmierer bemerkte einmal: „Jeder Dummkopf kann Code schreiben, den ein Computer versteht. Gute Programmierende schreiben Code, den Menschen verstehen können.“ ( "Any fool can write code that a computer can understand .Good Programmers write code that humans can understand", Kent Beck)

Unser Ziel ist es, das Programm verständlicher zu machen.

Da ein Uhrzeiger ein klar erkennbares Konzept darstellt, wäre es eine gute Möglichkeit, die Klarheit des Programms zu verbessern, indem man es in eine Klasse umwandelt. Erstellen wir eine ClockHand-Klasse, die einen Uhrzeiger beschreibt, der Informationen über seinen Wert, die obere Grenze (d. h. den Punkt, an dem der Wert des Zeigers wieder auf null zurückgesetzt wird) enthält und Methoden zum Fortschritt des Zeigers, zur Anzeige seines Werts und zur Ausgabe des Werts in String-Form bereitstellt.

public class ClockHand {
    private int value;
    private int limit;

    public ClockHand(int limit) {
        this.limit = limit;
        this.value = 0;
    }

    public void advance() {
        this.value = this.value + 1;

        if (this.value >= this.limit) {
            this.value = 0;
        }
    }

    public int value() {
        return this.value;
    }

    public String toString() {
        if (this.value < 10) {
            return "0" + this.value;
        }

        return "" + this.value;
    }
}

Sobald wir die ClockHand-Klasse erstellt haben, ist unsere Uhr klarer geworden. Es ist nun einfach, die Uhr auszugeben, d. h. den Uhrzeiger, und der Fortschritt des Zeigers ist in der ClockHand-Klasse verborgen. Da der Zeiger mithilfe der von der ClockHand-Klasse definierten oberen Grenzvariablen automatisch zum Anfang zurückkehrt, ist die Funktionsweise der Zeiger etwas anders als in der Implementierung, die int-Variablen verwendet. Diese prüfte, ob der Wert der Ganzzahl, die den Uhrzeiger darstellte, die obere Grenze überschritt, wonach sein Wert auf null gesetzt und der Wert der Ganzzahl, die den nächsten Uhrzeiger darstellte, inkrementiert wurde. Mit Uhrzeigerobjekten schreitet der Minutenzeiger voran, wenn der Wert des Sekundenzeigers null ist, und der Stundenzeiger schreitet voran, wenn der Wert des Minutenzeigers null ist.

ClockHand hours = new ClockHand(24);
ClockHand minutes = new ClockHand(60);
ClockHand seconds = new ClockHand(60);

while (true) {
    // 1. Printing the time
    System.out.println(hours + ":" + minutes + ":" + seconds);

    // 2. Advancing the second hand
    seconds.advance();

    // 3. Advancing the other hands when required
    if (seconds.value() == 0) {
        minutes.advance();

        if (minutes.value() == 0) {
            hours.advance();
        }
    }
}

Die objektorientierte Programmierung geht in erster Linie darum, Konzepte in ihre eigenen Einheiten zu isolieren oder mit anderen Worten, Abstraktionen zu erstellen. Trotz des vorherigen Beispiels könnte man es als sinnlos erachten, ein Objekt zu erstellen, das nur eine Zahl enthält, da dies auch direkt mit int-Variablen gemacht werden könnte. Dies ist jedoch nicht immer der Fall.

Ein Konzept in eine eigene Klasse zu trennen, ist aus vielen Gründen eine gute Idee. Erstens können bestimmte Details (wie z. B. das Drehen eines Zeigers) innerhalb der Klasse verborgen werden (d. h. abstrahiert). Anstatt eine if-Anweisung und eine Zuweisungsoperation zu schreiben, genügt es, dass die Person, die den Uhrzeiger verwendet, eine klar benannte Methode advance() aufruft. Der resultierende Uhrzeiger kann auch als Baustein für andere Programme verwendet werden - die Klasse könnte zum Beispiel CounterLimitedFromTop genannt werden. Das heißt, eine Klasse, die aus einem klaren Konzept erstellt wurde, kann für verschiedene Zwecke verwendet werden. Ein weiterer großer Vorteil besteht darin, dass, da die Implementierungsdetails des Uhrzeigers für seine Nutzerinnen und Nutzer nicht sichtbar sind, diese bei Bedarf geändert werden können.

Wir haben erkannt, dass die Uhr drei Zeiger enthält, d. h., sie besteht aus drei Konzepten. Die Uhr ist ein eigenes Konzept. Daher können wir auch für sie eine eigene Klasse erstellen. Als Nächstes erstellen wir eine Klasse namens Clock, die die Zeiger in sich verbirgt.

public class Clock {
    private ClockHand hours;
    private ClockHand minutes;
    private ClockHand seconds;

    public Clock() {
        this.hours = new ClockHand(24);
        this.minutes = new ClockHand(60);
        this.seconds = new ClockHand(60);
    }

    public void advance() {
        this.seconds.advance();

        if (this.seconds.value() == 0) {
            this.minutes.advance();

            if (this.minutes.value() == 0) {
                this.hours.advance();
            }
        }
    }

    public String toString() {
        return hours + ":" + minutes + ":" + seconds;
    }
}

Die Funktionsweise des Programms ist zunehmend klarer geworden. Wenn Sie unser Programm unten mit dem ursprünglichen Programm vergleichen, das aus Ganzzahlen bestand, werden Sie feststellen, dass die Lesbarkeit des Programms überlegen ist.

Clock clock = new Clock();

while (true) {
    System.out.println(clock);
    clock.advance();
}

Die oben implementierte Uhr ist ein Objekt, dessen Funktionalität auf "einfacheren" Objekten basiert, d. h. auf ihren Zeigern. Dies ist genau die große Idee hinter der objektorientierten Programmierung: Ein Programm wird aus kleinen und klar abgegrenzten Objekten aufgebaut, die zusammenarbeiten.

Loading

Als nächstes überprüfen wir die Begriffsterminologie.

Objekt

Ein Objekt bezieht sich auf eine unabhängige Einheit, die sowohl Daten (Instanzvariablen) als auch Verhalten (Methoden) enthält. Objekte können in vielen verschiedenen Formen auftreten: Einige können Konzepte aus dem Problemfeld beschreiben, andere werden verwendet, um die Interaktionen zwischen Objekten zu koordinieren. Objekte interagieren miteinander durch Methodenaufrufe - diese Methodenaufrufe werden sowohl verwendet, um Informationen von Objekten anzufordern, als auch um ihnen Anweisungen zu geben.

Im Allgemeinen hat jedes Objekt klar definierte Grenzen und Verhaltensweisen und ist sich nur der Objekte bewusst, die es zur Erfüllung seiner Aufgabe benötigt. Mit anderen Worten: Das Objekt verbirgt seine internen Vorgänge und bietet Zugang zu seiner Funktionalität über klar definierte Methoden. Darüber hinaus ist das Objekt unabhängig von jedem anderen Objekt, das es nicht zur Erfüllung seiner Aufgabe benötigt.

Im vorherigen Abschnitt haben wir uns mit Objekten beschäftigt, die Personen darstellen, deren Struktur in einer „Person“-Klasse definiert war. Es ist eine gute Idee, sich daran zu erinnern, was eine Klasse macht: Eine Klasse enthält den Bauplan, der benötigt wird, um Objekte zu erstellen, und definiert auch die Variablen und Methoden der Objekte. Ein Objekt wird auf der Grundlage des Klassenkonstruktors erstellt.

Unsere Personenobjekte hatten Eigenschaften wie Name, Alter, Gewicht und Größe sowie einige Methoden. Wenn wir weiter über die Struktur unseres Personenobjekts nachdenken, könnten wir sicherlich weitere Variablen in Bezug auf eine Person finden, wie eine persönliche Identifikationsnummer, Telefonnummer, Adresse und Augenfarbe.

In Wirklichkeit können wir alle möglichen verschiedenen Informationen und Dinge mit einer Person in Verbindung bringen. Wenn wir jedoch eine Anwendung erstellen, die sich mit Personen beschäftigt, werden die Funktionalitäten und Merkmale, die mit einer Person verbunden sind, basierend auf dem Anwendungsfall der Anwendung gesammelt. Beispielsweise würde eine Anwendung, die sich auf persönliche Gesundheit und Wohlbefinden konzentriert, wahrscheinlich die zuvor genannten Variablen wie Alter, Gewicht und Größe verfolgen und auch die Möglichkeit bieten, einen Body-Mass-Index und eine maximale Herzfrequenz zu berechnen. Andererseits könnte eine Anwendung, die sich auf Kommunikation konzentriert, die E-Mail-Adressen und Telefonnummern von Personen speichern, aber keine Informationen wie Gewicht oder Größe benötigen.

Der Zustand eines Objekts ist der Wert seiner internen Variablen zu einem bestimmten Zeitpunkt.

In der Programmiersprache Java würde ein Personenobjekt, das Name, Alter, Gewicht und Größe verfolgt und die Möglichkeit bietet, den Body-Mass-Index und die maximale Herzfrequenz zu berechnen, wie folgt aussehen. Unten sind Höhe und Gewicht als Double dargestellt - die Längeneinheit ist ein Meter.

public class Person {
    private String name;
    private int age;
    private double weight;
    private double height;

    public Person(String name, int age, double weight, double height) {
        this.name = name;
        this.age = age;
        this.weight = weight;
        this.height = height;
    }

    public double bodyMassIndex() {
        return this.weight / (this.height * this.height);
    }

    public double maximumHeartRate() {
        return 206.3 - (0.711 * this.age);
    }

    public String toString() {
        return this.name + ", BMI: " + this.bodyMassIndex()
            + ", maximum heart rate: " + this.maximumHeartRate();
    }
}

Die Bestimmung der maximalen Herzfrequenz und des Body-Mass-Index einer bestimmten Person ist mit der oben beschriebenen Person-Klasse unkompliziert.

Scanner reader = new Scanner(System.in);
System.out.println("What's your name?");
String name = reader.nextLine();
System.out.println("What's your age?");
int age = Integer.valueOf(reader.nextLine());
System.out.println("What's your weight?");
double weight = Double.valueOf(reader.nextLine());
System.out.println("What's your height?");
double height = Double.valueOf(reader.nextLine());

Person person = new Person(name, age, weight, height);
System.out.println(person);
Beispielausgabe

What's your name? Napoleone Buonaparte What's your age 51 What's your weight? 80 What's your height? 1.70 Napoleone Buonaparte, BMI: 27.68166089965398, maximum heart rate: 170.03900000000002

Klasse

Eine Klasse definiert die Arten von Objekten, die daraus erstellt werden können. Sie enthält Instanzvariablen, die die Daten des Objekts beschreiben, einen oder mehrere Konstruktoren, die zur Erstellung verwendet werden, und Methoden, die das Verhalten des Objekts definieren. Nachfolgend wird eine Rechteckklasse beschrieben, die die Funktionalität eines Rechtecks definiert.

// class
public class Rectangle {

    // instance variables
    private int width;
    private int height;

    // constructor
    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    // methods
    public void widen() {
        this.width = this.width + 1;
    }

    public void narrow() {
        if (this.width > 0) {
            this.width = this.width - 1;
        }
    }

    public int surfaceArea() {
        return this.width * this.height;
    }

    public String toString() {
        return "(" + this.width + ", " + this.height + ")";
    }
}

Einige der oben definierten Methoden geben keinen Wert zurück (Methoden, die das Schlüsselwort void in ihrer Definition enthalten), während andere einen Wert zurückgeben (Methoden, die den Typ der zurückzugebenden Variablen angeben). Die Klasse oben definiert auch die Methode toString, die den String zurückgibt, der zum Ausgeben des Objekts verwendet wird.

Objekte werden durch Konstruktoren mit dem Befehl new aus der Klasse erstellt. Im Folgenden erstellen wir zwei Rechtecke und geben dazugehörige Informationen aus.

Rectangle first = new Rectangle(40, 80);
Rectangle rectangle = new Rectangle(10, 10);
System.out.println(first);
System.out.println(rectangle);

first.narrow();
System.out.println(first);
System.out.println(first.surfaceArea());
Beispielausgabe

(40, 80) (10, 10) (39, 80) 3920

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