OOP Beginnershandleiding (PHP5)

  1. Inleiding
  2. Object geörienteerd denken
  3. Foute denkwijze
  4. Object georiënteerd programmeren
  5. Visibility
  6. Naamgeving
  7. Constructor __construct()
  8. Voorbeeld: HTML tabel
  9. Inheritance
  10. Voorbeeld: HTML tabel 2 (inheritance)
  11. Static methods en properties
  12. Abstract classes en Interfaces
  13. Magic methods
  14. Slotwoord en referenties
  15. Reacties op deze tutorial

Abstract classes en Interfaces

In dit hoofdstuk bekijken we een ander krachtig onderdeel van OOP, namelijk het gebruik van abstract classes en interfaces. Beide middelen zijn bedoeld om de programmeur (jijzelf, of iemand anders) te dwingen bepaalde methods of properties te gebruiken. Op die manier kun je vooraf bepalen hoe bepaalde classes gebruikt dienen te worden of in een applicatie opgenomen dienen te worden.

Abstract classes
Een abstract class is een class met of zonder eigen properties en een aantal methods die gedeeltelijk de functionaliteit van de class bepalen maar tegelijkertijd een deel van de functionliteit onbepaald laat. Het onbepaalde gedeelte zijn de abstract methods en deze dienen uitgewerkt te worden in de child class die deze abstract class extend.

Deze lastige definitie is eigenlijk alleen maar goed uit te leggen met een voorbeeld, dus laten we de User class er weer eens bijpakken. Stel je nu de situatie voor dat je een webshop aan het bouwen bent waarbij je twee verschillende typen gebruikers kent: klanten en werknemers. In het hoofdstuk over inheritance hebben we gezien hoe de User class te extenden is tot een Customer class, maar het grote nadeel is dat er in dat geval nog steeds een User object aangemaakt kan worden waar je eigenlijk niets mee kan, we hebben immers alleen klanten en werknemers geen gebruikers zonder functie. Om dat te voorkomen definiëren we de User class nu als abstract, hetgeen ondermeer betekent dat hij niet geïnstantieerd kan worden.
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
abstract class User {
    private 
$_username;

      public function 
__construct($name) {
        
$this->_username $name;
    }

    public function 
getUsername() {
        return 
$this->_username;
    }
    
    public abstract function 
getUserStatus();
}
?>

Deze class komt ons inmiddels bekend voor, maar de abstract method getUserStatus() is nieuw. Dat deze method als abstract gedeclareerd is, betekent dat het de verantwoordelijkheid is van de child class om voor de functionaliteit van getUserStatus() te zorgen. Deze method moet wel abstract zijn omdat de functionaliteit verschillend is bij de child classes.

De twee classes die we nu nog missen, Customer en Employee, zijn beide een child van de User class. Beide classes worden dus gedwongen om minimaal de getUserStatus() method te definiëren.
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class Customer extends User {
    private 
$_customerId;
    
    public function 
__construct($username$id) {
        
$this->_username $username;
        
$this->_customerId $id;
    }
    
    public function 
getUserStatus() {
        return 
'customer';
    }
}

class 
Employee extends User {
    private 
$_employeeId;
    
    public function 
__construct($username$id) {
        
$this->_username $username;
        
$this->_employeeId $id;
    }
    
    public function 
getUserStatus() {
        return 
'employee';
    }
}
?>

De twee classes lijken (nog) erg veel op elkaar, het belangrijke verschil zit hem echter in de getUserStatus() method. Als we het geheel samenvoegen met de User class en nog een paar regels procedurele code toevoegen, is dit het resultaat:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
abstract class User {
    private 
$_username;

      public function 
__construct($name) {
        
$this->_username $name;
    }

    public function 
getUsername() {
        return 
$this->_username;
    }
    
    public abstract function 
getUserStatus();
}

class 
Customer extends User {
    private 
$_customerId;
    
    public function 
__construct($username$id) {
        
$this->_username $username;
        
$this->_customerId $id;
    }
    
    public function 
getUserStatus() {
        return 
'customer';
    }
}

class 
Employee extends User {
    private 
$_employeeId;
    
    public function 
__construct($username$id) {
        
$this->_username $username;
        
$this->_employeeId $id;
    }
    
    public function 
getUserStatus() {
        return 
'employee';
    }
}

$jan = new Customer('jan'1);
$inge = new Employee('inge'1);

echo 
'Jan is een '.$jan->getUserStatus().'. <br />';
echo 
'Inge is een '.$inge->getUserStatus().'.';
?>

Code: output
1
2
Jan is een customer. 
Inge is een employee.

Zoals aan de output te zien is, doet de getUserMethod() wat van hem gevraagd wordt.

Dit voorbeeld geeft zeer eenvoudig de werking van abstract classes weer. Op deze manier kun je vooraf een gemeenschappelijk gedeelte van meerdere classes programmeren om deze abstract class later te extenden met de classes die je daadwerkelijk gaat gebruiken. Het grote voordeel: de gemeenschappelijke functionaliteit hoef je maar een keer te programmeren.

Interfaces
Een interface is een overeenkomst tussen ongerelateerde objecten voor het uitvoeren van dezelfde functionaliteit. Een interface stelt je in staat om aan te geven dat een object een bepaalde functionaliteit moet bezitten, maar het bepaalt niet hoe het object dat moet doen. De child class is dus vrij om de hele implementatie te doen, zolang hij maar voldoet aan de functionaliteit die de interface afdwingt.

In het geval van een interface extend de child class de parent niet, maar implementeert hij hem. Daartoe maken we gebruik van het keyword 'implements'.

Stel dat we bezig zijn met het ontwikkelen van een applicatie die de communicatie met verschillende type databases moet kunnen afhandelen. Bekend is dat de ene database anders werkt dan de ander en dat vaak verschillende (PHP) functies nodig zijn. Het is onmogelijk om één class te schrijven die met alle type databases werkt, sterker nog voor elke database heb je een aparte class nodig. Maar we kunnen wel vooraf de functionaliteit bepalen die elke database class minimaal moet hebben, ongeacht de database waarmee we werken. Dat zou er als volgt uit kunnen zien:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
interface Database 

    public function 
connect(); 
    public function 
error(); 
    public function 
errno(); 
    public function 
escape($string); 
    public function 
query($query); 
    public function 
fetchArray($result); 
    public function 
fetchRow($result); 
    public function 
fetchAssoc($result); 
    public function 
fetchObject($result); 
    public function 
numRows($result); 
    public function 
close(); 
}
?>

Deze interface dwingt elke class die hem implementeert om minimaal functionaliteit toe te kennen aan deze methods. Bovendien moet de child class bij elke method minimaal de parameters accepteren die in de interface bepaald zijn. Een method mag meer parameters hebben, zolang ze optioneel zijn, maar zeker niet minder.

Een child class die de communicatie met een MySQL database kan afhandelen, zou er als volgt uit kunnen zien:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php 
class Mysql_Database implements Database {
    private 
$_link;
    
    public function 
connect($server=''$username=''$password=''$new_link=true$client_flags=0) {
        
$this->_link mysql_connect($server$username$password$new_link$client_flags); 
    }
    
    public function 
error() {
        return 
mysql_errno($this->_link); 
    }
    
    public function 
errno() {
        return 
mysql_error($this->_link);  
    }
    
    public function 
escape($string) {
        return 
mysql_real_escape_string($string$this->_link);  
    }
    
    public function 
query($query) {
        return 
mysql_query($query$this->_link);  
    }
    
    public function 
fetchArray($result$array_type MYSQL_BOTH) {
        return 
mysql_fetch_array($result$array_type);  
    }
    
    public function 
fetchRow($result) {
        return 
mysql_fetch_row($result);  
    }
    
    public function 
fetchAssoc($result) {
        return 
mysql_fetch_assoc($result); 
    }
    
    public function 
fetchObject($result) {
        return 
mysql_fetch_object($result);  
    }
    
    public function 
numRows($result) {
        return 
mysql_num_rows($result); 
    }
    
    public function 
close() {
        return 
mysql_close($this->_link); 
    }
}
?>

Dit is de functionaliteit die door de Database interface afgedwongen wordt en elke database class moet bezitten. Er zijn echter veel meer mysql functies dus deze class zou verder uitgebreid kunnen worden om hem beter aan te laten sluiten op de MySQL functionaliteit. Deze selectie van methods is echter voor elke database te implementeren, daarom worden ze afgedwongen door de interface.

De procedurele code om te communiceren met de database zou er nu als volgt uit kunnen zien:
Code
1
2
3
4
5
6
7
8
9
10
11
<?php
$db 
= new Mysql_Database();
$db->connect('host''username''password'); 
$db->query('USE webshop');

$result $db->query("SELECT username FROM users"); 
         
while(
$row $db->fetchAssoc($result)) { 
    echo(
$row['username']); 
}
?>

Als we de classes voor andere databases geschreven hebben, kunnen we in dit voorbeeldje eenvoudig van database wisselen door enkel de eerste regel te veranderen. Voor een postgreSQL database zou dat bijvoorbeeld zo kunnen zijn:
Code
1
2
3
<?php
$db 
= new Postgresql_Database();
?>


Verschil tussen abstract classes en interfaces
Ze lijken erg op elkaar, maar er zijn een aantal belangrijke verschillen tussen abstract classes en interfaces.

Abstract classes
  • Een abstract class kan bepaalde functionaliteit definiëren en de rest overlaten aan de child.
  • Een child kan de reeds gedefinieerde methods overschrijven, maar hoeft dat niet.
  • De child class moet een logische relatie hebben met de parent.
  • Een child kan maximaal een abstract class extenden.

Interfaces
  • Een interface kan geen functionalteit bevatten. Het is enkel een definitie van de methods die gebruikt moeten worden.
  • De child class moet alle methods uit de interface van functionaliteit voorzien.
  • Verschillende niet gerelateerde classes kunnen op een logische manier gegroepeerd worden door een interface.
  • Een child kan meerdere interfaces tegelijkertijd implementeren.

Vorige Volgende