10.14 Vererbung 

Elternklasse, Kindklasse
Die Objektorientierung stellt das Konzept der Vererbung zur Verfügung. Zum Beispiel erben die Klasse Auto und die Klasse Fahrrad alle Methoden der Klasse Fahrzeug . Wenn die Klasse Fahrzeug z. B. die Methode anzahl_raeder hat, so erben die Klassen Fahrrad und Auto diese Methode. Die Klassen Fahrrad und Auto können weitere Methoden definieren oder sogar die geerbte Methode überschreiben, indem die Methode neu definiert wird. Die Klasse Fahrzeug wird auch Elternklasse genannt und die Klassen Fahrrad und Auto Kindklassen .
Abbildung UML-Diagramm einer Vererbung
Das Diagramm zeigt an, dass Bicycle und Car die Attribute wheels und seats von der Klasse Vehicle erben. Zusätzlich besitzt die Klasse Bicycle noch das Attribut wheel_size, und die Klasse Car besitzt zwei weitere Attribute.
Keine Vererbung
Relationale Datenbanksysteme kennen das Konzept der Vererbung nicht direkt. Es ist z. B. in einem relationalen Datenbanksystem nicht direkt möglich, eine Tabelle zu definieren, die von einer anderen Tabelle die Felder erbt.
STI
Durch eine Technik, die sogenannte Single Table Inheritance (abgekürzt STI), kann eine Vererbung in einem relationalen Datenbanksystem abgebildet werden. Dazu wird eine Tabelle erstellt, die die Felder aller beteiligten Klassen erhält. Außerdem wird ein zusätzliches Feld type benötigt, das den Namen der Klasse speichert (z. B. Bicycle oder Car).
In unserem Beispiel benötigen wir eine Tabelle mit den Feldern wheels, seats, wheel_size, speed, cylinder und type . Gegebenenfalls können Sie vorher auch ein neues Projekt mit rails vererbung erstellen.
ruby script/generate model vehicle wheels:integer seats:integer wheel_size:integer speed:integer cylinder:integer type:string
Das generierte Migration-Skript enthält bereits alle erforderlichen Felder. Vor Ausführung des Migration-Skripts mit rake db:migrate können Sie noch weitere Ergänzungen vornehmen.
Listing db/migration/001_create_vehicle
class CreateVehicles < ActiveRecord::Migration def self.up create_table :vehicles do |t| t.integer :wheels t.integer :seats t.integer :wheel_size t.integer :speed t.integer :cylinder t.string :type t.timestamps end end def self.down drop_table :vehicles end end
Eine Tabelle
Ohne Vererbung (bzw. STI) hätten wir eine Tabelle cars und eine Tabelle bicycles erstellen müssen. Mit STI wird hingegen eine große Tabelle verwendet.
Das Skript generiert neben den Migration-Skripts auch die Model-Datei vehicle.rb im Verzeichnis app/models.
Listing app/models/vehicle.rb
class Vehicle < ActiveRecord::Base # Hier können Methoden hinzugefügt werden, # die auf alle Kindklassen vererbt werden. end
In diesem Verzeichnis erstellen wir noch zwei weitere Model-Klassen manuell. Diese Klassen sind direkte Kindklassen von der Klasse Vehicle.
Listing app/models/bicycle.rb
class Bicycle < Vehicle
# Es können individuelle Methoden hinzugefügt werden
end
Listing app/models/car.rb
class Car
< Vehicle
# Es können individuelle Methoden hinzugefügt werden
end
Beispiele
Wenn ein neuer Datensatz erstellt wird, so wird automatisch das Feld type auf den Namen der Klasse gesetzt. In der Rails-Konsole kann der Umgang mit STI demonstriert werden. Achten Sie bei den Ausgaben auf das Feld type.
ruby script/console >> Bicycle.create(:wheels=>2, :seats=>2, :wheel_size=>28) => #<Bicycle id: 1, wheels: 2, seats: 2, wheel_size: 28, speed: nil, cylinder: nil, type: nil, ...> >> Car.create(:wheels=>4,:seats=>5,:speed=>220,:cylinder=>6) => #<Car id: 2, wheels: 4, seats: 5, wheel_size: nil, speed: 220, cylinder: 6, type: "Car", ...> >> Car.create(:wheels=>4,:seats=>2,:speed=>320,:cylinder=>12) => #<Car id: 3, wheels: 4, seats: 2, wheel_size: nil, speed: 320, cylinder: 12, type: "Car", ...>
Die Klassen-Methoden, wie z. B. find und count, beziehen sich immer nur auf den entsprechenden Typ. Der Ausdruck Car.count zählt z. B. nur die Datensätze, die im Feld type den Wert Car enthalten. Wenn die Klassen-Methoden hingegen auf die Elternklasse angewendet werden, so werden alle Datensätze einbezogen.
>> Bicycle.count => 1 >> Car.count => 2 >> Vehicle.count => 3
Wenn man über eine Elternklasse auf ein Objekt z. B. über die Methode find zugreift, so wird die Klasse automatisch angepasst.
>> Vehicle.find(1).class.to_s => "Bicycle" >> Vehicle.find(2).class.to_s => "Car"
Man kann bei STI nicht verhindern, dass Objekte Felder verwenden, die für diese nicht bestimmt sind.
Bicycle.create(:cylinder=>2)
Das stellt in der Praxis jedoch meist kein Problem dar, da die Eingaben für den Endbenutzer über Formulare erfolgt. In den Templates können Sie dann selbst bestimmen, welche Felder verwendet werden.
Wenn die beteiligten Klassen jedoch nur sehr wenig gemeinsame Attribute besitzen, so ist STI weniger gut geeignet. In diesem Fall können polymorphe Assoziationen verwendet werden.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.