Objektorientert programmering

Vi har egentlig brukt objektorientert programmering (OOP) og klasser i de fleste programmene våre, uten at vi har snakket om hva det er, og hvordan det virker. Mye av hensikten med å bruke OOP er gjøre koden vår ryddigere og mer gjenbrukbar. Som nevnt i Wikipedia-teksten under, har OOP avanserte konsepter, som abstraksjon og polymorfi, men vi vil prøve å holde dette så enkelt som mulig. Siden vil forklare litt om konseptene, og vi vil bygge en veldig enkel klasse. 

Den første gangen vi traff på objektorientering, var når vi brukte objektet Serial for å skrive til serieporten. Serial er et standard-objekt i Arduino, og vi trenger ikke å definere eller opprette dette objektet. I Setup-funksjonen brukte vi metoden Serial.begin, og ellers i programmet brukte vi metodene Serial.print og Serial.println.

I tillegg er alle bibliotekene vi har brukt satt opp som klasser, som vi lager objekter fra, og bruker disse objektenes metoder.

En metode er omtrent det samme som en funksjon, men i tilknytning til klasser og objekter kalles det altså en metode. 

En klasse kan ha både egne variabler og metoder, og disse kan være enten private eller offentlig tilgjengelig (public)

Bakgrunn (Fra Wikipedia):
Konseptet stammer fra arbeidet nordmennene Kristen Nygaard og Ole-Johan Dahl gjorde ved Norsk Regnesentral med programmeringsspråket Simula på 1960-tallet, noe de ble belønnet med både Turing-prisen og John von Neumann-medaljen for.
OOP-konseptet fikk stor utbredelse gjennom bruk i andre programmeringsspråk, på 1970-tallet Smalltalk, på 1980-tallet C++ og på 1990-tallet Java.

Følgende prinsipper er sentrale i OOP:
Objekter – pakke data og funksjonalitet sammen i enheter i programmet. Dette er basis for modularitet, en av kvalitetene man prøver å oppnå.
Abstraksjon – gjøre at programmereren underveis kan ignorere noen av detaljene ved implementasjon av det som jobbes med.
Innkapsling – skjule den interne tilstanden til et objekt fra andre. Dette gjør at utenforstående kode ikke kan endre på tilstanden til objektet på uforutsette måter.
Polymorfi – gjøre at et objekt kan oppføre seg som et annet, bare den oppfyller den «kontrakten» grensesnittet spesifiserer.
Arv – lette arbeidet med innkapsling og polymorfi ved å tillate programmereren å lage objekter som er mer spesialiserte utgaver av andre objekter.

De fleste av de mest brukte programmeringsspråkene i dag benytter seg av en objektorientert programmeringsmodell.

 

Klasser


En klasse er en måte å kombinere variabler og funksjonalitet til ett objekt. En klasse kan representere et fysisk objekt, som et kjøretøy, en motor, en sensor osv., men den kan også representere noe abstrakt, som for eksempel en bankkonto. Bankkontoen er ikke noe vi kan ta og føle på, men den har likevel egenskaper, som eier, saldo osv.

Når vi har definert (eller importert) en klasse, kan vi så lage ett eller flere objekter basert på klassen. 

For å definere en klasse bruker vi ordet class og angir et klasse-navn:

  class MinKlasse {

    ….

    ….

  }

Som det meste i C++, må det som tilhører klassen "omkranses" med klammeparanteser, som vist over.

Klassen består som regel av både variabler og metoder (funksjoner). Disse kan som nevnt være både private og offentlig tilgjengelig. Når det gjelder variabler, er det tilrådelig at disse holdes private, slik at de ikke kan endres utenfra uten å bruke en av klassens metoder

Som et lite eksempel lager vi en liten klasse for lysdioder. Ikke så veldig nyttig, men et eksempel som gjør nytten.

Den første kodebolken viser selve klassedefinisjonen. Denne beskriver hvilke variabler klassen har, og hvilke metoder som kan brukes på et objekt av denne klassen. Variabler eller metoder som ligger under private: kan ikke nå utenfra, men kan endres eller brukes inne i selve klassen eller objektet. Det som ligger under public: er det som er tilgjengelig for brukeren av klassen.

class Lysdiode {
private:
int _pinne;
int _blinkFrekvens;
public:
Lysdiode(int pinne);
Lysdiode(int pinne, int frekvens);
void skruPaa();
void skruAv();
void blink(int antall);
};
 

Metoder


Neste kodebolk inneholder metodene (funksjonene) til klassen. De to første er litt spesielle. Dette er to versjoner av constructor-metoden. Constructoren er en spessiel metode som kjøres hver gang et objekt av denne klassen opprettes. I dette tilfellet har vi også brukt det vi kaller "overloading". Det betyr at vi har mer enn en versjon av en metode eller funksjon, som har forskjellig antall parametere. Hvilken av metodene som kjøres, avhenger av hvor mange parametere som sendes med i funksjonskallet.

Lysdiode::Lysdiode(int pinne) {
_pinne = pinne;
pinMode(_pinne, OUTPUT);
_blinkFrekvens = 10;
}

//Et eksempel på "Overloading". Constructoren har to forskjellige versjoner,
//en med 1 parameter og 1 med 2 parametre
Lysdiode::Lysdiode(int pinne, int frekvens) {
_pinne = pinne;
pinMode(_pinne, OUTPUT);
_blinkFrekvens = frekvens;
}

void Lysdiode::skruPaa() {
digitalWrite(_pinne, HIGH);
}

void Lysdiode::skruAv() {
digitalWrite(_pinne, LOW);
}

void Lysdiode::blink(int antall) {
for (int teller = 0; teller < antall; teller++) {
skruPaa();
delay(1000/(2*_blinkFrekvens));
skruAv();
delay(1000/(2*_blinkFrekvens));
}
}

 

 

Objekter:

I den siste kodebolken oppretter vi to objekter fra klassen. Her trenger vi ikke å definere pinnene som utganger i setup, fordi det gjøres i constructoren.
For den røde lysdioden sender vi også parameteren for å sette blinkefrekvensen.

Lysdiode blaaLed(3); //Blå lysdiode på pinne 3
Lysdiode roedLed(5,3); //Rød lysdiode på pinne 5, med blinkfrekvens 3Hz

void setup() {

}

void loop() {
blaaLed.skruPaa(); //Tenner den blå lysdioden
delay(400); //Pause på 400 millisekunder
blaaLed.skruAv(); //Slukker den blå lysdioden
delay(400); //Pause på 400 millisekunder

blaaLed.blink(10); //Blå lysdiode blinker 10 ganger, i 10 Hz, som er standardinnstillingen.
roedLed.blink(20); //Rød lysdiode blinker 20 ganger, i 3 Hz, som angitt når vi opprettet objektet
}

Komplett program:

Her er alle kodebolkene satt sammen til et komplett program: 

class Lysdiode {
private:
int _pinne;
int _blinkFrekvens;
public:
Lysdiode(int pinne);
Lysdiode(int pinne, int frekvens);
void skruPaa();
void skruAv();
void blink(int antall);
};

Lysdiode::Lysdiode(int pinne) {
_pinne = pinne;
pinMode(_pinne, OUTPUT);
_blinkFrekvens = 10;
}

//Et eksempel på "Overloading". Constructoren har to forskjellige versjoner,
//en med 1 parameter og 1 med 2 parametre
Lysdiode::Lysdiode(int pinne, int frekvens) {
_pinne = pinne;
pinMode(_pinne, OUTPUT);
_blinkFrekvens = frekvens;
}

void Lysdiode::skruPaa() {
digitalWrite(_pinne, HIGH);
}

void Lysdiode::skruAv() {
digitalWrite(_pinne, LOW);
}

void Lysdiode::blink(int antall) {
for (int teller = 0; teller < antall; teller++) {
skruPaa();
delay(1000/(2*_blinkFrekvens));
skruAv();
delay(1000/(2*_blinkFrekvens));
}
}

Lysdiode blaaLed(3); //Blå lysdiode på pinne 3
Lysdiode roedLed(5,3); //Rød lysdiode på pinne 5, med blinkfrekvens 3Hz

void setup() {

}

void loop() {
blaaLed.skruPaa(); //Tenner den blå lysdioden
delay(400); //Pause på 400 millisekunder
blaaLed.skruAv(); //Slukker den blå lysdioden
delay(400); //Pause på 400 millisekunder

blaaLed.blink(10); //Blå lysdiode blinker 10 ganger, i 10 Hz, som er standardinnstillingen.
roedLed.blink(20); //Rød lysdiode blinker 20 ganger, i 3 Hz, som angitt når vi opprettet objektet
}

Oppgaver:

Et par enkle oppgaver:

Kommer...