Hauptinhalt
Programmierung
Kurs: Programmierung > Lerneinheit 1
Lektion 17: Objektorientiertes DesignWiederholung: Objektorientiertes Design
Dies ist eine Wiederholung von dem, was wir in diesem Tutorial über objektorientiertes Design gelernt haben.
Wenn wir Programme erstellen, wollen wir oft viele verschiedene Objekte mit ähnlichen Eigenschaften erstellen. Wie z. B. viele Katzen, welche alle eine etwas andere Fell-Farbe und Größe haben. Oder viele Buttons mit verschiedenen Beschriftungen und Positionen. Wir wollen die Möglichkeit haben zu sagen "Normalerweise sieht eine Katze so aus" und dann sagen "und diese, diese und diese Katze haben gewisse Eigenschaften gleich und ein paar andere ganz anders". In diesem Fall verwenden wir objektorientiertes Design, um Objekttypen zu definieren und Instanzen dieser Objekte zu verwenden.
Um in JavaScript einen Objekttypen zu definieren, müssen wir zuerst den "Konstruktor" deklarieren. Dies ist eine Funktion, welche wir immer dann verwenden, wenn wir eine neue Instanz eines Objekttypes erstellen wollen. Hier der Konstruktor für den Objekttyp
Book
, welcher ein Buch darstellt:var Book = function(title, author, numPages) {
this.title = title;
this.author = author;
this.numPages = numPages;
this.currentPage = 0;
};
Die Funktion akzeptiert Parameter für die Aspekte, welche für jedes Buch anders sein können, den Titel, Autor und die Anzahl Seiten (title, author, numPages). Dann initialisiert sie die Eigenschaften des Objektes mit den Werten dieser Parameter, indem sie das Schlüsselwort
this
verwendet. Wenn wir this
in einem Objekt verwenden, dann beziehen wir uns auf die aktuelle Instanz eines Objektes. Das Objekt referenziert sich selbst. Wir müssen diese Eigenschaften auf this
speichern, damit wir später wieder auf diese zugreifen können.Um eine Instanz des Objektes
Book
zu erstellen, deklarieren wir eine neue Variable, um diese zu speichern. Dann benutzen wir das Schlüsselwort new
gefolgt von einem Aufruf des Konstruktors und übergeben diesem die erwarteten Parameter für die Eigenschaften des neuen Objektes.var book = new Book("Robot Dreams", "Isaac Asimov", 320);
Mit der Punktnotation können wir danach auf die Eigenschaften des neuen Objektes zugreifen:
println("I loved reading " + book.title); // I loved reading Robot Dreams
println(book.author + " is my fav author"); // "Isaac Asimov" is my fav author
Wir wollen dies nun kurz genauer anschauen. Was wäre denn passiert, wenn wir unseren Konstruktor nicht korrekt implementiert hätten:
var Book = function(title, author, numPages) {
};
var book = new Book("Little Brother", "Cory Doctorow", 380);
println("I loved reading " + book.title); // I loved reading undefined
println(book.author + " is my fav author"); // undefined is my favorite author
Wenn wir die Parameter dem Konstruktor übergeben, aber darin diese nicht explizit auf dem Objekt
this
speichern, dann können wir später nicht mehr auf diese zugreifen! Das Objekt vergisst diese, sobald der Konstruktor ausgeführt ist.Wenn wir Objekttypen definieren, wollen wir oft Eigenschaften und das Verhalten mit ihnen verknüpfen. Zum Beispiel sollen alle unsere Katzenobjekte miauen() und fressen() können. Daher müssen wir Funktionen mit unseren Objekttypen verbinden. Dies machen wir, indem wir diese auf dem sogenannten Prototyp eines Objektes definieren:
Book.prototype.readItAll = function() {
this.currentPage = this.numPages;
println("You read " + this.numPages + " pages!");
};
Dies ist genau gleich, wie wir auch sonst eine Funktion deklarieren. Aber anstatt diese global zu deklarieren, deklarieren wir sie auf dem Prototypen von
Book
. So weiß JavaScript, dass diese Funktion auf Objekten des Typs Book
aufgerufen werden kann und dass diese Funktion Zugriff auf this
des Objektes hat, für welches sie aufgerufen wurde.So eine Funktion nennen wir, nun da sie zu einem Objekt gehört, Methode. Wir können sie folgendermaßen aufrufen:
var book = new Book("Animal Farm", "George Orwell", 112);
book.readItAll(); // You read 112 pages!
Beachte, mit objektorientiertem Design wollen wir es uns einfach machen, mehrere verwandte Objekte (Objektinstanzen) zu erstellen. Schauen wir uns an, wie der Code dazu aussieht:
var pirate = new Book("Pirate Cinema", "Cory Doctorow", 384);
var giver = new Book("The Giver", "Lois Lowry", 179);
var tuck = new Book("Tuck Everlasting", "Natalie Babbit", 144);
pirate.readItAll(); // Du hast 384 Seiten gelesen!
giver.readItAll(); // Du hast 179 Seiten gelesen!
tuck.readItAll(); // Du hast 144 Seiten gelesen!
Mit diesem kurzen Stück Code erstellen wir drei Instanzen von Book. Diese haben alle die gleichen Eigenschaften, welche aber trotzdem unterschiedlich sind. Schön!
Nun, wenn du dir aber die reale Welt anschaust, dann sind Katzen und Hunde verschiedene Objekte. Daher würdest du wohl beim Programmieren einer
Katze
und eines Hundes
unterschiedliche Objekttypen deklarieren. Eine Katze würde miauen()
und ein Hund würde bellen()
. Aber beide sind sich trotzdem ähnlich, denn beide können fressen()
und haben ein Alter
und ein Geburtsdatum
und ein Todesdatum
. Beide sind Säugetiere. Dies bedeutet, dass sie vieles gemeinsam haben, obwohl sie unterschiedliche Tiere sind.In diesem Fall verwenden wir die Idee der Objektvererbung. Ein Objekttyp kann Eigenschaften und Verhalten (Methoden) an abgeleitete Objekttypen vererben. Die abgeleiteten Objekte können aber weiterhin ihre eigenen Eigenschaften und Methoden hinzufügen. All die
Katzen
und Hunde
können von Säugetiere
abgeleitet werden. So müssten sie die Methode fressen()
nicht neu erfinden. Aber wie machen wir dies in JavaScript?Gehen wir zurück zu unserem Beispiel mit Büchern und sagen wir, dass
Book
der zugrundeliegende Objekttyp ist und wir die Objekttypen Paperback
und EBook
davon ableiten.Ein Taschenbuch (Paperback) ist ähnlich wie ein Buch, hat aber zumindest in unserem Programm einen Unterschied: Es hat ein Titelbild. Wegen dieser zusätzlichen Information muss unser Konstruktor nun vier Parameter akzeptieren.
var PaperBack = function(title, author, numPages, cover) {
// ...
}
Wir wollen nun aber nicht all den Code aus dem Konstruktor von
Book
wiederholen. Wir wollen den Vorteil, dass dieser Code gleich ist, wahrnehmen. Wir können den Konstruktor von Book
aus dem Konstruktor von PaperBack
aufrufen und ihm die notwendigen drei Parameter übergeben.var PaperBack = function(title, author, numPages, cover) {
Book.call(this, title, author, numPages);
// ...
};
Wir müssen aber immer noch die Eigenschaft
cover
im Objekt speichern. Wir brauchen dafür eine zusätzliche Zeile:var PaperBack = function(title, author, numPages, cover) {
Book.call(this, title, author, numPages);
this.cover = cover;
};
Nun da wir einen Konstruktor für unser
PaperBack
haben, der uns hilft die Eigenschaften von Book
wiederzuverwenden, wollen wir nun aber auch die Methoden an PaperBack
vererben. Und so sagen wir dem Programm, dass der Prototyp von PaperBack
vom Prototypen von Book
abgeleitet werden soll:PaperBack.prototype = Object.create(Book.prototype);
Vielleicht wollen wir auch Taschenbuch-spezifische Verhalten, wie die Möglichkeit, es zu verbrennen, definieren. Dies machen wir, indem wir zusätzliche Funktionen auf dem Prototypen nach der Zeile oben definieren:
PaperBack.prototype.burn = function() {
println("Omg, du hast alle " + this.numPages + " Seiten verbrannt");
this.numPages = 0;
};
Und jetzt können wir ein neues Taschenbuch erstellen, es lesen und auch verbrennen (burn)!
var calvin = new PaperBack("The Essential Calvin & Hobbes", "Bill Watterson", 256, "http://ecx.images-amazon.com/images/I/61M41hxr0zL.jpg");
calvin.readItAll(); // Du hast 256 Seiten gelesen!
calvin.burn(); // Wow! du hast alle 256 Seiten verbrannt!
(Gut, wir werden es nicht wirklich verbrennen, da es ein großartiges Buch ist, aber vielleicht dann, wenn wir einmal in einer Eisspalte feststecken und uns verzweifelt nach Wärme sehnen.)
Und nun versehst du, wie man objektorientiertes Design in JavaScript benutzt, um für deine Programme komplexe Daten und Modelle zu erstellen, um die Welt um dich herum abzubilden.
Willst du an der Diskussion teilnehmen?
Noch keine Beiträge.