Hvad er OOP?

OOP er et akronym for Object Orientated Programming.
På dansk oversættes det til Objekt Orienteret Programmering.
OOP er et stort emne og det vil være omsonst at forsøge at dække alle aspekter af OOP i en enkelt artikel.
Med andre ord, artiklen her vil dække et snævert område, men nok til at du ved hvad OOP er og kan.

Artiklen er opdelt således:
  1. Hvad er OOP?
  2. Forskellen mellem OOP og ikke-OOP
    1. De er defineret forskelligt
    2. De er defineret forskelligt
    3. De har forskellige antal fungerende kopier
    4. De har forskellige tilgange
    5. De håndterer deres tilstande forskelligt
  3. Praktiske eksempler
    1. Erklæring af variabler og typer
    2. Indkapsling
    3. Nedarvning
    4. Polymorfi
  4. Konklusion

Hvad er OOP?

Objekt Orienteret Programmering er programmering orienteret omkring objekter hvilket giver dig fordelene af indkapsling, polymorfi og nedarvning for at optimere kodegenbrug og minimere vedligholdelsen af koden.

Lød det teknisk? Vi kommer til at gå de forskellige emner igennem, så bare rolig...

Forskellen mellem OOP og ikke-OOP

En bedre måde at forklare forskellen på er ved at komme med faktiske eksempler.

De er defineret forskelligt

Når en funktion defineres skal den navngives unikt - altså, navnet må ikke gå igen i din applikation.

<?php

    
function fNavn($arg1$arg2)
    {
        ...
        return 
$result;
    }
    
?>

En klasses metode defineres inden for en klasses grænser. Dets scope. Hver klassenavn skal navngives unikt i din applikation. Hver klasse må indeholde en vilkårligt antal metoder (funktioner) og funktionen skal navngives unikt i klassen, men ikke i applikationen. Faktisk, så er det den egenskab at forskellige klasser kan dele funktioner/metoder der gør at de kan har egenskaber om polymorfi.

<?php

    
class cNavn
    
{
    
        public function 
fNavn($arg1$arg2)
        {
            ...
            return 
$result;
        }
    }
    
?>
De tilgås forskelligt

Det er vigtigt at understrege at hverken funktioner eller klasser kan tilgås før deres definitioner er blevet indlæst.

At kalde en funktion er ret simpelt:

<?php

    $resultat 
fNavn($arg1$arg2);
    
?>

At tilgå en klasses metode er lidt mere kompliceret. Først skal klassen instancieres og derefter kan metoden/funktionen kaldes:

<?php

    
//Klassen instancieres
    
$object = new cNavn();
    
    
//Metoden/funktionen tilgås
    
$result $object->fNavn($arg1$arg2);
    
?>
De har forskellige antal fungerende kopier

Funktioner skal ikke instancieres for at kunne tilgås, derfor siger man at der kun er en kopi (eller instans) ad gangen.

En klasses metode kan kun tilgås når den er blevet instancieret, og det er muligt at danne flere instanser af det samme object med forskellige navne.

<?php

    $object1 
= new cNavn();
    
$object2 = new cNavn();
    
$object3 = new cNavn();
    
?>
De har forskellige tilgange

En funktion har kun en tilgang, og det er funktionsnavnet i sig selv.

Et object har flere tilgange, en for hver metode/funktion i klassen.

De håndterer deres tilstande forskelligt

Per definition har en funktion ikke en tilstand; forstået på den måde at for hver gang du tilgår en funktion, kaldes den som var den ny og ikke som en fortsættelse af en tidligere tilgang.

Et object en tilstand; forstået på den måde at hver gang du tilgår et objekts metode agerer metoden ud fra den tilstand du efterlod objektet i efter en tidligere tilgang.

Det er muligt for både funktioner og klassers metoder at bruge lokale variabler (lokale i deres eget scope) og de fungerer begge på samme måde. Det betyder at de variabler der er i scopet af funktionen eller metoden ikke er tilgængelig uden for disse scopes.

Det er muligt for en funktion at huske værdier mellem hver tilgang ved at erklære en variabel for at være statisk ved at bruge operatoren static.

<?php

    
function taelOp()
    {
        static 
$a 0;
        
$a++;
        return 
$a;
    }
    
?>

For hver gang du kalder funktionen vil funktionen returnere et tal der større end det forrige. Uden static vil du altid få '1' retur.

Klasse variabler der skal tilgås uden for metoder i klassen defineres på klasse niveau.

<?php

    
class Lommeregner
    
{
        private 
$tal;
        
        public function 
plus($value)
        {
            
$this->tal $this->tal $value;
            
            return 
$this->value;
        }
        
        public function 
minus($value)
        {
            
$this->tal $this->tal $value;
            
            return 
$this->value;
        }
        
        public function 
hentTal()
        {
            return 
$this->value;
        }
        
    }
    
?>

Bemærk at alle klasse/objekt variabler referes til med præfixet $this-> som i $this->variabelnavn. Enhver variabel uden dette præfix anses for at kun eksistere i metodens/funktions scope, altså som en lokal variabel.

Bemærk også at hver instans af et objekt er ansvarlig for sine egne variabler, således at indholdet af et objekt er helt uafhængigt af indholdet i et andet objekt.

Praktiske eksempler

Lad os gennemgå nogle praktiske eksempler som også domonstrerer indkapsling, nedarvning og polymorfi.

Erklæring af variabler og typer

Grundlæggende vil det være vigtigt at vide hvad forskellen er på de forskellige måder at erklære variabler og funktionerne på.
Når du 'erklærer' en variabel, skal det forstås sådan at du opretter en variabel og tilknytter den en form for værdi. Det er muligvis meget basal viden for de fleste, men der er altid nogen som er i tvivl om betydningen.

Ligeledes erklærer du også en funktion ved at fortælle PHP at du skal lave en funktion. Et bar pasale ekspempler:

<?php

    
// Her erklæres en variabel
    
$min_variabel "mit indhold";
    
    
// Her erklæres en funktion
    
function minFunktion()
    {
        echo 
"indholdet af min funktion";
    }

?>

Når du arbejder med klasser vil du støde på tre ord som oftest vil gå igen i forbindelse med disse erklæringer. Det er ordende public, private og protected.

Disse ord vil også blive brugt i de følgende praktiske eksempler.

Selve betydningen af dem er måske ikke så åbenlys, men de fortæller faktisk dit PHP script hvordan dine funktioner og variabler må bruges.
public fortæller din kode at variablen/funktionen må tilgås af alt og alle. Det vil sige at når du har et instantieret objekt, kan du tilgå objektets variabler og funktioner fra din omkringliggende kode.
Tager vi således udgangspunkt i eksemplet fra lommeregneren fra før, vil du kunne se at alle objektets funktioner kan tilgås alle steder fra:

<?php

    $lommeregner 
= new Lommeregner();
    
$lommeregner->plus(5);
    
$lommeregner->minus(2);
    echo 
$lommeregner->hentTal();
    
?>

Dog er klassens variabel $tal erklæret for private hvilket gør at den kun er tilgængelig i selve objektet.
Det vil altså sige at du kan ikke kan tilgå den variabel på andre måder en gennem dit objekts funktioner. Og prøver du, vil dit script fejle - præcis som det skal.

protected er så en smule speciel - den fungerer faktisk lidt ligesom private. Den eneste forskel er at private kun kan tilgås i den klasse de er oprettet i, mens at protected også kan tilgås i nedarvede klasser.
Nedarvning er et emne som der dækkes om lidt, hvor du også vil kunne se at variabler i kapitlets praktiske eksempel er erklæret for at være protected.

Jeg håber det var med til at skabe lidt klarhed over disse nøgleord til de kommende eksempler.

Indkapsling
Indkapsling En struktur der placerer data og funktionalitet der udføres på de data i den samme klasse. Klassen bliver så 'indkapslingen' eller en beholder for dataene og funktionaliteterne.

Enhver applikation skal håndtere et antal forskellige elementer eller 'ting' såsom 'kunder', 'produkter' og 'faktura', så det er meget almindeligt at man har en klasse for hver af disse ting. Under brugen af applikationen vil man så danne en eller flere instanser/objekter fra hver af disse klasser og når applikationen så skal håndtere en af disse elementer, vil den gøre det ved at tilgå den relevante metode på det relevante objekt.

De data hvert objekt indeholder under kørslen vil ikke blive gemt i hukommelsen for evigt, så der vil du typisk bruge et data lager (som f.eks. en database som MySQL). Opbygningen af denne kan variere, men lad os antage at vi har en tabel for hver af førnævnte elementer. Der er fire basale funktioner som kan udføres i en database (opret, læs, opdater og slet), så vi laver en klasse med en metode for hver af disse funktionaliteter.

<?php

    
class Produkter
    
{
        private 
$database;
        private 
$tabel;
        private 
$fieldlist;
        
        
        
        
/**
        * Metoden __construct() vil altid eksekveres når du instancierer et objekt.
        * Denne metode kaldes en konstruktør (constructer).
        */
        
        
public function __construct()
        {
            
$this->database "eksempel";
            
$this->tabel "produkter";
            
$this->fieldlist = array("kolonne1""kolonne2""kolonne3""kolonne4")
        }
        
        public function 
hent($feltArray)
        {
            ...
            return 
$feltArray;
        }
        
        public function 
indsaet($feltArray)
        {
        
            ...
            return 
$feltArray;
        }
        
        public function 
slet($feltArray)
        {
        
            ...
            return 
$feltArray;
        }
        
        public function 
opdater($feltArray)
        {
        
            ...
            return 
$feltArray;
        }
        
        
    }

?>

Bemærk at:

  1. Klassens konstruktør identificerer databasen og den tabel der skal benyttes, hvilket bliver det der adskiller denne fra de andre databaser og tabeller. Indholdet i konstruktøren udføres automatisk når du instansierer objektet.
  2. Al tabel data ligger i et enkelt array istedet for en seperat variabel for hvert felt.
  3. Jeg har ikke inkluderet den faktiske kode til at udføre disse funktioner, da det vil være meget omfattende i forhold til formålet med artiklen

En sådan klasse agerer så som 'indkapsling' af både data for et element og de funktioner der kan udføres på de data. Det er indkapsling.

Nedarvning
Nedarvning Genbrug af grundlæggende klasser (superklasser) til af forme afledte klasser (underklasser). Metoder og indhold defineret i superklassen bliver automatisk en del af underklassen.

Efter have skrevet en klasse til mit element for Produkter og et til mit element for kunder kiggede jeg på hvilke metoder og hvilket indhold de havde tilfælles, og hvilke der var unikke og ikke kunne deles. Så ville jeg tage de ting klasserne havde til fælles og danne en 'superklasse'.

For at danne superklassen ændrede jeg navnet på klassen og ændrede lidt i konstruktøren.

<?php

    
class Standard
    
{
        protected 
$database;
        protected 
$tabel;
        protected 
$fieldlist;
                
        public function 
__construct()
        {
            
$this->database "";
            
$this->tabel "";
            
$this->fieldlist = array()
        }
        
        public function 
hent($feltArray)
        ...
        
        public function 
indsaet($feltArray)
        ...
        
        public function 
slet($feltArray)
        ...
        
        public function 
opdater($feltArray)
        ...
        
        
    }

?>

Denne klasse kan ikke instansieres til et fungerende objekt idet der ikke referes til en database eller tabel der eksisterer, så dette er hvad man kalder en 'abstrakt klasse'.

Dernæst, ændrede jeg hver klasse for mine elementer; jeg fjernede de funktioner jeg kunne genbruge og inkluderede nøgleordet extend for at tvinge min klasse til at arve fra min superklasse.

<?php
    
include("standard.obj.php");

    class 
Produkter extends Standard
    
{
                
        public function 
__construct()
        {
            
$this->database "eksempel";
            
$this->tabel "produkter";
            
$this->fieldlist = array("produktId""produktNavn""produktPris""produktAntal")
        }
        
        public function 
hent($feltArray)
        
        public function 
indsaet($feltArray)
        
        public function 
slet($feltArray)
        
        public function 
opdater($feltArray)
        
        
    }

?>

Når en underklasse bliver instansieret til et objekt, vil objektet indeholde alle metoder og variabler samt indhold fra superklassen lige så vel som underklassen. Hvis du har defineret noget i både superklassen og underklassen vil underklassens definitioner blive brugt.

I et reelt udviklingsmiljø vil disse eksempler naturligvis indeholde forholdsvis meget kode i forhold til ovenstående eksempler. Formålet var at vise at du med en superklasse kan undgå at gentage kode, men blot skrive koden en gang og lade adskillige underklasser arve koden. Nedarvning er derfor en meget værdifuld metode at lave en enkelt udgave af fælles funktionalitet tilgængelig for flere objekter fremfor at have adskillige kopier af den fælles kode.

Polymorfi
Polymorfi Samme interface, forskellig implementering. Muligheden for at erstatte en klasse for anden. Det betyder at forskellige klasser kan indeholde de samme metode-navne, men resulterne fra hver af metoderne vil være forskellige iddet koden bag hver metode (implementeringen) er forskellig i hver klasse.

Polymorfi kan kun implementeres hvor den samme metode eksisterer i flere klasser. Det betyder at den samme metode kan bruges i forskellige objekter, men vil give et andet resultat idet at koden bag metoden er forskellig fra klasse til klasse.

Eksempelvis kan vi tage en række klasser kaldet 'Produkter', 'Kunder' og 'Faktura'. En praksis man ser der umuliggør polymorfi er at navngive metoderne ud fra klassenavnet. For eksempel:

  1. hentProdukter(), indsaetProdukter(), sletProdukter(), opdaterProdukter()
  2. hentKunder(), indsaetKunder(), sletKunder(), opdaterKunder()
  3. hentFaktura(), indsaetFaktura), sletFaktura(), opdaterFaktura()

Problemet her er at den kontrollerende instans skal kende metodens navn for hver af instanserne. Det gør det unødvendigt kompliceret at kontrollere de forskellige objekter.

Vi fortsætter med eksemplerne fra før, hvor metoderne var navngivet hent(), indsaet(), slet(), opdater().

Da vi i forvejen har angivet disse i superklassen bliver det meget let at arbejde med gennem nedarvningen.

Fordelen ved det er at jeg kan have en enkelt kontrollerende instans for hver af mine elementer og den kontrollerende instans kan arbejde med hvilket som helst af mine elementer.
Eksempel:

<?php
    
...
    include(
$element.".obj.php");
    
$object = new $element;
    
$data $object->hent($felter);
    ...
?>

Indholdet af $element og $felter bliver gjort tilgængelige under kørslen.

Det der er relevant er at elementernes klasser ikke er hard-codede ind, hvilket gør din applikation meget mere fleksibel.

Konklusion

Der er faktisk kun tre ting der virkelig differentierer OOP fra ikke-OOP og det er indkapsling, nedarvning og polYmorfi. Alt andet er hype og misforståelser.

Et højt niveau af genbrug og minimalt vedligehold kan ikke garanteres - det er helt afhængigt af hvordan teknikkerne implemtenteres.