Grundkurs i C-Sharp - Polymorphism
Förord
Till att börja med så rekommenderar jag att om ni inte redan har gjort det, eller redan har kunskaper inom områdena, att läsa min introduktionsartikel för ärvning (inheritance) samt för gränssnitt (interface), då denna artikel använder sig av dessa koncept. Vad är då polymorphism? I kort är det en benämning på att kunna hantera en rad olika klasser, med olika implementeringar, på samma sätt i en kodsnutt. Ni som har läst mina artiklar om ärvning och gränssnitt kommer säkert ihåg att jag nämnde möjligheten att skapa generiska metoder för att hantera en rad olika klasser, och det är precis det som polymorphism är.Innehåll
»»
»
Relaterade artiklar
» Grundkurs i C-Sharp - Arv» Grundkurs i C-Sharp - Gränssnitt
» Grundkurs i C-Sharp - Introduktion
» Grundkurs i C-Sharp - Namnrymder
» Grundkurs i C-Sharp - Statiska medlemmar
» Grundkurs i C-Sharp - Strukturer
» Grundkurs i C-Sharp - Undantagshantering
» Grundkurs i C-Sharp - Åtkomstmodifiering
» Grundkurs i C-Sharp - Överlastning
Användning
Situationerna där polymorphism är användbart är många och jag är övertygad om att ni kommer att utnyttja detta åtskilliga gånger i era program. För ett ge ett konkret exempel så kan ni föreställa er ett program som hanterar försäljningen av diverse fordon, där vi för enkelhetens skulle begränsar oss till försäljning av bilar, båtar och flygplan.Med detta som utgångspunkt letar vi fram lämpliga klass-kandidater till det program vi skall skriva. Vi har redan nu tre givna kandidater, nämligen de tre olika typer av fordon som vi skall kunna administrera försäljningen av. Nedan ser ni skeletten till de klasserna.
class Boat
{
}
class Car
{
}
class Plane
{
}
Det kan även vara lämpligt att skapa en klass som representerar en kund, så denna är en aktör i vårt system. Så innan vi går vidare skapar vi ett skelett för vår kund klass.
class Customer
{
}
Vad som mer behövs för att de kunder som behandlas av systemet skall kunna genomföra något vettigt kan det vara lämpligt att han/hon kan placera en order som innehåller de olika fordon som skall beställas.
För att möta detta behov skapar vi ytterligare en klass.
class Order
{
}
För att inte göra exemplet för invecklat väljer vi att inte gå djupare in på de krav som finns på vårt program, utan vi kommer under resterande delar av artikeln att koncentrera oss på hur en order skulle kunna hanteras av vårt program.
Låt oss ta en närmare titt på vad våra kunder skulle kunna tänka sig vilja göra. Rent impulsivt skulle jag vilja sammanfatta hans interaktion med programmet att täcka följande (jag hoppas att ni har överseende med att detta är en rent fiktiv kund med ett enormt bankkonto).
- Köpa bila.
- Köpa båtar
- Köpa flygplan
Detta betyder att ett odefinierat antal av varje fordonstyp skall kunna placeras i en order. Det kan vara lämpligt att kunna ta bort ett fordon från ordern om kunden skulle ändra sig. Vi bygger ut vår order-klass så att den uppfyller dessa krav.
class Order
{
private Boat[] boats = new Boar[];
private Car[] cars = new Car[];
private Plane[] planes = new Planes[];
public void AddBoat(Boat b)
{
// Lägg till Boat objektet b i vektorn boats.
// Kod utelämnad för enkelhetens skull.
}
public void AddCar(Car c)
{
// Lägg till Car objektet c i vektorn cars.
// Kod utelämnad för enkelhetens skull.
}
public void AddPlane(Plane p)
{
// Lägg till Plane objektet pi vektorn planes.
// Kod utelämnad för enkelhetens skull.
}
public void RemoveBoat(Boat b)
{
// Ta bort Boat objektet b i vektorn boats.
// Kod utelämnad för enkelhetens skull.
}
public void RemoveCar(Car c)
{
// Ta bort Car objektet c i vektorn cars.
// Kod utelämnad för enkelhetens skull.
}
public void RemoveBoat(Plane p)
{
// Ta bort Plane objektet p i vektorn planes.
// Kod utelämnad för enkelhetens skull.
}
}
Nu har vi en order-klass som kan hantera att man lägger till och tar bort fordon av olika typer till en order. Men hur skulle vi lösa det i framtiden om vi var tvungen att utöka vårt program så att det även hanterade försäljning av motorcyklar? Då skulle vi få lägga till ytterligare en vektor av typen Motorcycle samt de två metoderna för att lägga till och ta bort motorcyklar i en order.
Eller vad skulle hända om vi bestämmer oss för att lägga till en metod i vår order-klass för att beräkna den totala summan för en order? Först och främst skulle vi få lägga till ett Price attribut (i denna artikel väljer jag att skapa ett public fält i klasserna, men en bättre design hade varit att göra det private och använda sig av ett attribut för att styra åtkomsten) i våra fordons klasser.
class Boat
{
public int Price;
}
class Car
{
public int Price;
}
class Plane
{
public int Price;
}
Sen är det dags att implementera själva metoden som skall beräkna den totala summan på ordern.
class Order
{
// Äldre kod för order klassen är oförändrad med
// utelämnas här för enkelhetens skull.
public int CalculateTotal()
{
int total = 0;
// Iterera igenom alla Boat objekt och summera
// kostnaden.
for(int n = 0; n < boats.length-1; n++)
{
total += boats.GetValue(n).price;
}
// Iterera igenom alla Car objekt och summera
// kostnaden.
for(int n = 0; n < cars.length-1; n++)
{
total += cars.GetValue(n).price;
}
// Iterera igenom alla Plane objekt och summera
// kostnaden.
for(int n = 0; n < planes.length-1; n++)
{
total += planes.GetValue(n).price;
}
return total;
}
}
Som ni kan se är det här som det börjar bli riktigt jobbigt att jobba med de olika fordonstyperna och detta är en mycket enkel metod jämfört med vad man kan förvänta sig i ett riktigt program. Om vi vid ett senare tillfälle bestämmer oss för att, som jag tidigare tog upp, hantera motorcyklar skulle vi behöva modifiera vår order klass och uppdatera CalculateTotal metoden.
Som ni säkert redan har listat ut så är detta inte någon elegant eller speciellt skalbar lösning för att implementera något i stil med detta, och tänk då på vilket enkelt och litet exempel det är jämfört med hur många olika klasser som ett riktigt program skulle kunna arbete med.
Som sagt, något måste göras för att snygga till och effektivisera vår lösning, och det är här som polymorphism (i detta fall genom ärvning) kommer in i bilden. Skulle vi inte kunna hitta en gemensam nämnare för våra fordon? Det mest uppenbara är just precis det att vare sig det handlar om bilar, båtar eller flygplan så är de alla fordon. Så varför skapar vi inte en basklass som samtliga fordonstyper ärver ifrån? Låter som en bra lösning? Bra, då gör vi det.
class Vechicle
{
}
class Boat : Vechicle
{
public int price:
}
class Car : Vechicle
{
public int price:
}
class Plane : Vechicle
{
public int price:
}
Den observanta läsaren kanske frågar sig varför jag inte var så smart så att jag flyttade price fältet från samtliga subklasser till basklassen, eftersom de ändå ärver ner alla publika fält. Om du är en av de läsarna så gratulerar jag till att du var observant, för det är precis det som vi borde göra.
class Vechicle
{
public int price:
}
class Boat : Vechicle
{
}
class Car : Vechicle
{
}
class Plane : Vechicle
{
}
Så, nu har vi skapat oss en gemensam nämnare för våra klasser, nämligen basklassen, som vi kan utnyttja för att skriva om vår order-klass för att hantera fordonen med hjälp av polymorphism.
class Order
{
private Verhicle[] vechicles = new Vehicle[];
public void AddVehicle(Verhicle v)
{
// Lägg till Vehicle objektet v i vektorn vechicles.
// Kod utelämnad för enkelhetens skull.
}
public void RemoveVehicle(Verhicle v)
{
// Ta bort Vechicle objektet v i vektorn vechicles.
// Kod utelämnad för enkelhetens skull.
}
public int CalculateTotal()
{
int total = 0;
// Iterera igenom alla Vechivle objekt och summera
// kostnaden.
for(int n = 0; n < vechicles.length-1; n++)
{
total += vechicles.GetValue(n).price;
}
return total;
}
}
Nu har vi en order klass som utnyttjar polymorphism för att hantera en rad olika klasser (med möjligen helt skilda implementeringar – tänk på att man troligen kommer att utöka vare subklass av Vehicle med andra metoder och attribut för att beskriva fordonstypen mer i detalj). Skulle vi nu bestämma oss för att hantera motorcyklar också är det inte svårare än att skapa en ny subklass
class Motorcycle : Vechicle
{
}
Och utan att behöva skriva om så mycket som en rad kod i vår order-klass, så hanterar den automatiskt den nya fordonstypen.
Sammanfattning
Polymorphism kan ta en del tid att vänja sig vid och att ha i tanken varje gång man designar sina olika klasser, men när man väl tänker på det och har möjlighet att utnyttja det så är det en otroligt användbar metod att hantera objekt på.Som jag tidigare nämnt är det möjligt att implementera polymorphism med antingen hjälp av ärvning eller gränssnitt. Vilken metod som är mest lämplig avgörs av situationen och designen av det program man utvecklar och det finns inga gyllene regler att följa. Det är erfarenhet och kunskap som avgör. Jag vill påpeka att om man implementerar det med hjälp av gränssnitt, så kan man åstadkomma väldigt avancerade saker, eftersom en klass kan implementera multipla gränssnitt (men bara ärva från en basklass). Detta gör att ett och samma objekt kan ingå i flera olika polymorphism situationer där varje situation implementerar polymorphism med hjälp av endast ett gränssnitt var.
0 Kommentarer