Objekt Orienterad Programmering med C# Del 1: Introduktion till Objekt Orienterad Programmering
Innehållsförteckning
Introduktion
Detta är den tredje modulen i min serie om .NET och C#.
I denna modulen ska vi gå igenom vad Objekt Orienterad Programmering är för något och varför vi ska använda det samt hur vi arbetar med Objekt Orienterad Programmering i C#.
Den här modulen kommer att vara uppdelad i 5 delar
- Introduktion till Objekt Orienterad Programmering
2. Vi kommer även här se enkla exempel på klasser med C# - Arbeta med klasser och konstruktorer
- Hantera kopplingar mellan klasser
- Arv och polymorfism
- Interface programmering
I denna del ska vi fokusera på vad Objekt Orienterad Programmering är.
Objekt Orienterad Programmering (OOP)
Vad är Objekt Orientering?
I grund och botten så handlar objekt orientering om att vi organiserar våra applikationer som en samling av objekt som innehåller både data och beteende.
Bilden ovan visar en enkel beskrivning av en applikation som består av ett antal objekt.
Ett annat sätt att tänka är att vi bygger en applikation genom att sätta ihop ett antal komponenter(objekt) som tillsammans utgör vår applikation. Vill vi utöka applikationen med mer funktionalitet så kan vi lägga till nya objekt med just den funktionalitet som vi önskar eller behöver.
Den klassiska jämförelsen är Lego, med olika lego klossar så kan vi sätta ihop dessa i olika kombinationer för att till slut ha skapat ett hus, bil eller rymdskepp.
Objekt orientering är ett normalt sätt för oss människor för att kategorisera verkligheten omkring oss. Vi ser objekt varje dag och utan att tänka på det och omedvetet kategorisera vi dessa. Vi ser människor, barn, hundar, katter, bilar, bussar, spårvagnar osv...
Omedvetet så vet vi hur de flesta fungerar. Ta exemplet bilar, de flesta som har körkort vet hur man öppnar dörren till bilen och hur vi startar den. Vi vet också hur vi gasar för att komma iväg, de flesta vet även hur man växlar samt hur vi får stopp på bilen genom att bromsa. Däremot vet de flesta av oss inte hur det egentligen fungerar i detalj. Vad är det som gör att kraften förflyttar sig för att jag trycker lite extra på gasen via motorn till drivningen av bilen. Ärligt talat vi bryr oss inte så länge som det fungerar, om man nu inte är en bilmekaniker.
Beroende på objekten runt omkring oss vet vi oftast hur dessa fungerar och verkar.
Det blir lite annorlunda när vi som utvecklare ska objektifiera ett problem eller ett verkligt ting.
I den objekt orienterade världen så tvistas det ibland om vilka egenskaper som är nödvändiga vid objekt orientering. Vad som oftast brukar vara de egenskaper som anses nödvändiga är följande fyra:
- Identitet
- Betyder att data är kvantifierat till diskreta urskiljbara enheter som vi kallar för objekt
- I verkligheten kan vi enkelt urskilja ett objekt ifrån ett annat. Vi har oftast inga problem att urskilja en Volvo ifrån en Ford. I programmering är det inte lika lätt. Vi behöver förse våra objekt med en unik adress(handle) för att urskilja dem och att arbeta med dem.
- Klassificering
- Betyder att objekt med samma attribut(data) och beteende(operationer) grupperas till en klass
- Polymorfism
- Betyder egentligen många former som kommer ifrån grekiskans poly som betyder många och morfism som betyder former.
- Innebörden i programutveckling är att samma beteende(operation) kan bete sig olika i olika klasser.
- Arv
- Betyder att klasser som ingår i en hierarki kan dela attribut och beteende.
- En klass kan definieras som generell och sedan förfinas(specialiseras) ner i förfinade klasser. Även kallade för härledda klasser eller barn klasser.
Objekt Orienterad Programmering
OOP är en filosofi eller en programmeringsstil för att utveckla programvaror som följer Objekt Orienteringens principer. Jag brukar uttrycka det som att vi utvecklare måste ta till oss ett nytt "Mindset". Vi måste börja tänka i lite nya banor, vi måste börja tänka i koncept som objekt och komponenter. Detta betyder egentligen att vi måste lära oss att bryta ner ett större problem i mindre delar(objekt). Vi måste börja med att skapa abstraktioner av verklighetsbaserad eller abstrakta ting.😳
Vad betyder allt detta?
Låt oss ta ett exempel, de flesta av oss har sett en faktura i pappersformat även om det idag nästan enbart förekommer E-fakturor😁. Vi vet att en faktura innehåller information såsom:
- Fakturanummer
- Faktura datum
- Förfallodatum
- Fakturabelopp(vad som ska betalas)
- Mottagaren av fakturan
- Adress till mottagaren av fakturan
- Betalningsmottagaren avsändaren av fakturan
- Faktura rader
- Varje faktura rad innehåller information som vad som är inhandlat, mängd, antal samt fakturarads belopp
- osv...
För att omvandla denna faktiska informationen måste vi abstrahera(endast hantera det väsentliga) denna information för att kunna klassificera information till olika klasser.
I den enklaste formen så kanske en faktura behöver bestå av 3 klasser: kund, faktura information, fakturarads information. I mer komplexa fall, vilket oftast är fallet, så kanske det behövs 10 klasser för att representera en fysisk faktura i programkod.
Klasser
Vad är en klass?
Klasser är den kod som vi skapar för att beskriva och definiera den information och de operationer som vi har kommit fram till när vi har klassificerat de verklighetsbaserade eller abstrakta objekten.
Vi kan med andra ord beskriva det på följande sätt. En klass är en mall där vi definierar
- Vilken information behöver vi hantera. I OOP termer benämns detta som state eller på svenska tillstånd eller data.
- Vilka operationer, det vill säga vilken funktionalitet behövs det för att manipulera tillståndet i klassen. I OOP termer benämns detta som behavior eller på svenska beteende.
Ett annat sätt att beskriva klasser på är att en klass är ett block av kod som utgör en liten del av vår applikation, ett byggblock.
Genom att skapa flera klasser och sedan sätta ihop dessa tillsammans får vi ett antal byggblock som utgör vår applikation.
Nu vet vi vad en klass är för något. Vad är då ett objekt?
Vad är ett objekt?
En klass är enbart en mall skriven i kod som definierar tillstånd och beteende, precis som vi såg ovan. För att kunna använda en klass så måste vi på något sätt få en representation av klassen i minnet. Detta kallas för att instansiera en klass eller på engelska instantiate a class., resultatet är ett objekt som hamnar i minnet.
Det är objektet som skapats som vi manipulerar inte klassen. Klassen är bara mallen. Vi kan skapa flera objekt i minnet från samma klass. Ett objekt har vad som kallas för SBI.
Ett objekts SBI
- State/tillstånd/data
- State är värdena på det data som vi placerar i objektet.
- Varje objekt har sitt privata data som vi har definierat i klassen.
- Behavior
- Är den funktionalitet/operationer som vi definierade i klassen
- Identity
- Är den unika identitet(handle) som vi behöver för att hitta objektet i minnet. Är adressen till minnesplatsen där objektet är placerat.
Klasser kontra Objekt
Som vi har sett ovan så är klassen vår mall där vi definierar tillstånd och beteende. Objekt är kopian av klassen som vi får i minnet som vi kan manipulera. Vi kan skapa flera objekt i minnet ifrån en och samma klass. Se nedanstående bild.
Varför OOP?
Genom att förstå och använda objekt orienterade principer i applikationsutveckling så får vi en massa fördelar.
- Vi får en mer organiserad och strukturerad kod
- Leder till mer lättläst och flexibel kod
- Gör koden mer förändringsbar samt mer underhållsbar
- Tvingar oss att undvika "spagetti kod"
- Gör det lättare att skapa återanvändbar kod
- Skriv en gång och använd i flera applikationer
- Utöka funktionaliteten genom att skapa en ny klass utifrån en befintlig klass
Objekt Design
Objekt design eller objekt modellering är tekniken eller metodiken för hur vi analyserar en problemställning. Oftast utformad som en kravspecifikation ifrån kund eller beställare. Ofta kan detta kännas svårt och oöverkomligt första gången man ställs inför situationen att skapa en objektmodell. Som sedan skall leda till en klass definition.
Jag ska här försöka ge er några enkla knep som jag själv ALLTID följer när jag börjar analysera en problemställning.
Utgå ifrån problemställningen eller kravspecifikationen och
- Försök att identifiera alla substantiv
- Detta är oftast första kandidaterna till klasser
- Försök att identifiera alla verb
- Detta är vad som måste kunna utföras
- Blir oftast ett bra första steg till att beskriva de operationer som behövs
- Försök sedan hitta alla adjektiv
- Detta blir oftast ett bra första steg till vilken information, data, tillstånd som klasserna behöver hantera
Dokumentera Objekt Design med UML
I stora applikationer eller system behöver ha ett sätt att beskriva våra klasser och som vi kommer se längre fram även kunna beskriva en klass hierarki och vilka beroenden som våra klasser har av varandra.
Unified Modeling Language UML
UML är industristandarden för att beskriva t ex klasser och hierarkier. Vi kan använda UML till väldigt mycket mer än bara klass beskrivningar, vi kan dokumentera hela applikationer, objekt hierarkier osv...
UML underhålls och hanteras av Object Management Group(OMG). Ursprungligen utvecklad av Rational Software Corporation, idag IBM.
Idéerna till UML kommer ursprungligen ifrån Booch, Rumbaugh och Jacobson
Tanken är att en bild säger mer än tusen ord samt att om vi använder UML så har vi ett gemensamt vokabulär som alla inom programutveckling är bekant med.
Exempel på en klass representation i UML:
Vi kommer att ta detta stegvis igenom de olika delarna i Objekt Orienterad Programmering med C#.
För att kunna dokumentera så behövs det någon typ av verktyg för detta. Det finns en uppsjö av verktyg på marknaden för detta. Några är gratis några är billiga och några är dyra.
Själv sitter jag på en Mac och då är alternativen oftast betallösningar, men här är tre varianter som fungerar bra.
- Visio (för windows)
- Ingår oftast i Office prenumerationerna
- StarUML (för macOS, Windows samt Linux(Ubuntu eller Fedora)
- Kostar per år att använda
- Det är den jag använder
- Draw.io
- Är ett gratis online verktyg, som är väldigt bra
Skapa klasser i C#
Låt oss nu se hur vi kan omvandla all teori som vi gått igenom till mer praktisk användning. I C# använder vi typen class för att skapa nya klasser. Grundsyntaxen ser ut som i följer.
[AccessModifier] class ClassName [: [base class[]] [interfaces [,]]]
- ClassName är det namn vi ger vår klass
- [AccessModifier] är valfri och beskriver hur åtkomlig vår klass är ifrån andra delar av applikationen eller ifrån andra applikationer.
- public tillgänglig för alla överallt
- internal endast tillgängliga för klasser i samma assembly
- Ingen(standard) samma som internal
Assembly är vår kod inklusive klasser och funktionalitet som är kompilerat till en .exe eller .dll fil eller paket.
Exempel på en enkel klass:
Om vi tar och tittar lite på bilden ovan och tar det steg för steg.
- På första raden har vi klassen public class BankAccount. Vi skapar här en klass som heter BankAccount och som är tillgänglig överallt för alla.
- Innanför klass definitionen har vi två sektioner. I den första sektionen ser vi två stycken variabler, men tack vare att de är placerade direkt inuti klassen kallas detta för klassens fält. De är dessutom deklarerade som publika. Vilket gör dem tillgängliga för alla överallt. När vi definierar fält på detta sättet så har vi också definierat vilken information som vi behöver kunna hantera i vår klass. Detta är vårt tillstånd eller state.
- I den andra sektionen har vi vår operation för manipulering av tillståndet. Vi har döpt den till Deposit och den tar två argument, accountNumber och amount. I implementering av operationen så ser vi att vi ändrar på värdet för fältet Balance. Vi manipulerar tillståndet i klassen. I C# och i andra programspråk så kallas publika operationer för metoder eller methods.
Skapa objekt ifrån en klass
För att kunna använda ovanstående BankAccount klass i våra applikationer så måste vi skapa en kopia av klassen. Vi måste instansiera klassen och kopian representerar då ett objekt. En kopia av klassen!
Vi gör detta genom att använda nyckelorder new.
Exempel:
BankAccount account1 = new BankAccount();
BankAccount account2 = new BankAccount();
account1.Deposit("AQ12345", 500);
account1.Deposit("AW09753", 700);
Här ser vi nu att ifrån en och samma klass skapar vi två objekt. Varje objekt får sin egen adress i minnet. Det som vi såg tidigare är objektets handle
Klass medlemmar
I vårt ovanstående exempel med klassen BankAccount så såg vi att vi hade två fält och en metod. Fälten Balance och AccountNumber och metoden Deposit. När vi deklarerar fält och metoder inuti en klass så kallas de för medlemmar av klassen eller klass medlemmar(class members). Ibland så hör vi begrepp som instans medlemmar(instance members). Vad är då skillnaden? För bägge begreppen är associerade med fält och metoder som vi har skapat i klasserna.
- Instance Members
- Medlemmar som är definierade på klassen men som nås via ett instansierat objekt kallas för instance members eller instans medlemmar
- Static Members
- Är medlemmar som är definierade i klassen men som går att nå utan att skapa ett objekt.
- Dessa medlemmar måste vara definierade med ett speciellt nyckelord static
- static nyckelordet gör att medlemmar är direkt tillgängliga på klasserna och inte på objekten
Låt oss se på några exempel.
Exempel 1:
Detta är ett exempel på traditionella instans medlemmar
// Vi ser här att vi måste skapa en instans av vår BankAccount klass för att kunna komma åt metoden Deposit...
BankAccount account = new BankAccount();
account.Deposit("12345", 500);
public class BankAccount{
public string AccountNumber = "";
public double Balance;
public void Deposit(string accountNumber, double amount){
AccountNumber = accountNumber;
Balance += amount;
}
}
Exempel 2:
Här är ett exempel där nyckelordet static har använts för att göra medlemmarna tillgängliga på klass nivån.
// Här kan vi nu se att vi inte behöver använda new för att skapa en instans av klassen. Utan vi kan nå medlemmarna direkt på klassen.
BankAccount.Deposit("12345", 500);
// Observera nyckelordet static på alla klassmedlemmarna.
public class BankAccount
{
public static string AccountNumber = "";
public static double Balance;
public static void Deposit(string accountNumber, double amount)
{
AccountNumber = accountNumber;
Balance += amount;
}
}
Nu ska det väl tilläggas att det är inte speciellt ofta som vi som utvecklare behöver skapa klasser med tillgång till medlemmar direkt på klassen.
Däremot finns det flera klasser i .NET ramverket som är definierade med static medlemmar. Till exempel så är Console klassen i .NET definierad med static medlemmar så vi till exempel kan skriva följande utan att behöva skapa en instans av Console klassen:
Console.WriteLine("Hello World");
Varför Static Members?
Ovan beskrev jag vad skillnaden på instans och statiska medlemmar är för något. Jag var tydlig med att det är inte något som vi utvecklare använder speciellt ofta. Däremot så finns det som jag nämnde väldigt många klasser i .NET som är just statiska, både klasser men oftast metoder men även egenskaper.
Frågan är då varför skulle vi vilja använda statiska klasser, metoder eller egenskaper?
Svaret kan vara varierande, men de vanligaste argument är:
- Att kunna skapa en klass som ska simulera konceptet singleton
- Singleton betyder att det endast finns en kopia i minnet som hanterar alla anrop till metoder eller egenskaper.
- En del utvecklare hävdar att detta är just orsaken till förekomsten av statiska klass medlemmar.
- Jag däremot hävdar att detta är dålig design och vill man åstadkomma ett singleton beteende så finns det bättre och säkrare sätt att göra detta på. Vilket vi kommer att se längre fram när vi kommer in på server baserad webb utveckling.
- Det andra argumentet kan vara att klassen egentligen bara är en hjälp klass en så kallad utility klass. Som endast har som uppgift att returnera ett statisk värde. Exempel på detta hittar vi i .NET ramverket.
- DateTime.???
- DateTime har ett antal metoder och egenskaper som kan returnera dagens datum eller tid. Vi kan även använda metoder för att manipulera datum och tid. Varför ska vi då behöva skapa ett objekt varje gång vi behöver tillgång datum och tid. Då är det bättre att vi använder samma metod eller egenskap utan att behöva ta upp minnesplats för detta.
- Console.WriteLine(), som vi redan sett, som används för att skriva ut ett meddelande i konsolfönstret. Samma argument gäller här som för DateTime.
- DateTime.???
Avslutning
Detta var första delen i Objekt Orienterad Programmering med C#. Vi har nu förhoppningsvis fått en bättre förståelse för varför vi bör använda OOP när vi bygger applikationer. Samt fått en grundförståelse för begrepp som operationer och tillstånd. Även skillnaden på vad en klass är och vad objekt betyder.