Arv
Arv Vad är arv? Arv handlar om att klassificera d.v.s. att ange relationer mellan klasser och inte relationer mellan objekt. Arv gör det möjligt att skapa nya klasser utifrån befintliga, vilket är ett sätt att återanvända gammal kod. Arv är en grundläggande teknik för att organisera och skapa klasser.
Terminologi Basklass Härledd klass Arv UML Klassen som redan är definierad och som man vill ärva ifrån Härledd klass Klassen som ärver ifrån basklassen Kan även vara en basklass till andra klasser som ärver ifrån den. Arv Är mekanismen för denna teknik UML A C B Basklass Härledd klass Härledd klass
Arv När? Härledd klass ska representera något som är en sort av det basklassen representerar Inte för att återanvända koden Person Anställd Student Kund Chef Lärare Säljare Adress
Tre olika sett att se på arv Sedd som datatyp class Djur { protected String namn; protected double vikt; public void ät() { /* ... */ } public void sov() { /* ... */ } } class Fågel : Djur private double vingspann; public void flyg() { /* ... */ } class Fisk : Djur private double maxdjup; public void simma() { /* ... */ } Sedd som klass Sedd som mängder
Arv En klass kan ärva från högst en klass. En basklass får ha godtyckligt antal härledda klasser. Härledd klass kan alltså lägga till och omdefiniera, men inte ta bort. Det är grundprincip i objektorienterat arv att ett objekt av härledd klass har allt och kan allt som basklassobjekt har och kan. Det är ett slags objekt av basklassen. Men det kanske gör på ett annat sätt. Arv fungerar helt logiskt i hur många generationer som helst. Varje härledd klass är som om den innehöll allt den ärvde från sin basklass, och den utgör i sin tur utan problem basklass för ytterliggare härledda klasser.
Arv Klassen System.Object är grundläggande för alla klasser. Med andra ord är alla klasser implicit härledda från klassen System.Object. Person Anställd Student Kund Chef Lärare Säljare Object Adress
Åtkomst Härledda klasser Medlemmar i den här klassen kan komma åt icke privata medlemmar definierade i basklassen Publika data medlemmar i basklassen blir publika även i härledda klassen Protected data medlemmar i basklassen blir protected även i härledda klassen protected Mellanliggande skyddsnivå (public - private) Kan användas endast av basklassen och alla andra klasser som ärver från basklassen.
Nack- och fördelar med protected Nackdelar med protected Härledda klassen behöver inte använda egenskaper (set, get) för att ändra värden av protected data medlemmar (direkt åtkomst) Metoder i härledda klassen måste skrivas så att de beror av basklassens implementeringen (ändringar i basklassen medför ändringar i alla härledda klasser) x ändrar namn till xCoordinate Fördelar med protected Protected data medlemmar förbättrar prestanda Onödiga anrop till set och get behövs inte
Att rekommendera Deklarera basklassens data medlemmar som private Använd publika egenskaper och metoder så att härledda klassen kan komma åt data medlemmar i basklassen Anropa basklassens konstruktorn explicit i härledda klassen för att initiera variabler för basklassens objekt.
Exempel class Shape { private int x, y; public Shape() // tom-konstruktorn { } public Shape(int x, int y) // konstruktorn { this.x = x; this.y = y; } public double Area() // beräkna area { return 0.0; } }
Exempel I C# är det teknet ”:” för att låta en klass ärva från en annan class Circle : Shape { private int radius; public Circle() // konstruktorn { } public double Area() // beräkan area { return System.Math.PI * radius * radius; } }
Konstruktorer Konstruktorer ärvs inte. Konstruktor i härledda klasser har ett beteende att anropa närmaste basklass konstruktorn. När objektet av härledda klassen skapas, en kedja av konstruktoranrop påbörjas Härleda klassens konstruktorn anropar basklassens konstruktorn implicit, eller explicit Basklassens konstruktor exekveras först
base vid anrop av konstruktor I en konstruktor kan man anropa en konstruktor för den omedelbara basklassen (base) i en s.k. “initieringslista”. class Circle : Shape { int radius; public Circle (int x, int y, int radius) : base(x,y) this.radius = radius; } public Circle (int radius) : base(0, 0) class Shape { int x,y; public Shape (int x, int y) this.x = x; this.y = y; }
base vid anrop av basklassens metoder Andra metoder i basklassen än konstruktorn kan anropas var som helst inom en metod. För att nå överskuggade metoder i basklassen använder man base. class B : A { int b; public int f() int x = base.f(); return x + b; } class A { int a; public int f() return a; }
Destruktorer När Garbage Collector tar bort härledda klassens objekt från minne, GB anropar då klassens destruktorn och en kedja av anrop påbörjas GB anropar härledda klassens destuktorn Härledda klassens destruktor exekverar först, och när den exekverat anropar den närmaste basklass destruktor. Sist anropas Object-klassens destruktorn. Oavsett om man skriver destruktor eller inte, så kommer eventuell destruktor i basklassen att anropas. I klasser där Dispose implementeras bör Dispose metoden anropa basklassens Dispose om sådan finns. Viktigt! Destruktorer anropas aldrig explicit.
Arv kontra aggregering Arv ska endast användas när det logiska förhållandet mellan befintlig och ny klass verkligen är av den karaktären. Arv ska endast användas vid solklara fall Kom ihåg : en sorts Vid tvekan använd aggregering Befintliga klassens logik återanvänds genom att ett objekt utgör medlem Ställ frågan ”är detta verkligen en sorts av basklassen?”. Om svaret är solkart ja använd arv Osäker använd aggregering
Arv kontra aggregering Class Circle { int x, y; int radius; // diverse medlemmar } Class Cylinder : Circle { int height; // metoder för volym etc. } Exempel: Är en cylinder en slags cirkel? Eller kanske en cirkel en slags cylinder? Eller kanske är en cylinder ett specialfall av cirkel? Eller .. Det intressanta här är att en cylinder kan implementeras m.h.a. en cirkel, så koden ser anständig ut. Men logiken är fel. Aggregering Class Cylinder { int height; Circle myCircle; public Cylinder(Circle myCircle, int height) this.height = height; this.myCircle = myCircle; }