OOP syntaxen en werkingen
wat is het verschil tussen
abstract class ...
en
class ...
en moet ik nu de databaseConnect extenden uit de databaseConfig of moet ik hier de __construct gebruiken zoals __construct(databaseConfig)? of denk ik nu helemaal verkeerd?
2e vraag: Je configuratie geef je mee in je construct en sla je op een 'property'. Deze configuratie is een eigenschap van de verbinding.
Tip: met classes kun je zo denken:
Code (php)
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
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
<?php
abstract class Auto
{
abstract public function getBrand();
abstract protected function startMotor();
public function start()
{
$this->startMotor();
}
}
class Renault_Twingo extends Auto
{
public function getBrand()
{
return new Brand('Renault');
}
protected function startMotor()
{
$this->turnKeys();
$this->runFuelPump();
..
}
}
?>
abstract class Auto
{
abstract public function getBrand();
abstract protected function startMotor();
public function start()
{
$this->startMotor();
}
}
class Renault_Twingo extends Auto
{
public function getBrand()
{
return new Brand('Renault');
}
protected function startMotor()
{
$this->turnKeys();
$this->runFuelPump();
..
}
}
?>
oftewel: een Renault Twingo is in algemenere zin een auto met een standaard set eigenschappen en methoden.
Gewijzigd op 10/06/2012 21:18:44 door Terence Hersbach
De connect doe je dan in de construct van de configuratie.
Nee Jeroen, een configuratie heeft niks met een connectie te maken. Configuratie is een onderdeel van je verbinding, maar een configuratie is niet je verbinding. Dat zijn 2 totaal verschillende objecten.
In al deze dingen heb je de superklasse: dier, auto, opslagmethode. En je hebt de subklassen: hond, renault twingo, sessie. De subklassen extenden de superklasse.
OO bestaat eigenlijk uit 2 onderdelen:
- Object Geörienteerd scripten
- Flexibel en aanpasbaar maken van je code
Interfaces en abstracte klassen hebben met dat 2e te maken. Je wilt nooit iets dubbel hoeven schrijven. Daarmee bedoel ik dat je dit niet wilt:
Code (php)
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
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
<?php
class Admin
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->rang = 10;
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
}
class User
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->rang = 1;
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
}
?>
class Admin
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->rang = 10;
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
}
class User
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->rang = 1;
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
}
?>
De meeste van deze klassen bevatten dubbele code. Dat kan je oplossen door een superklasse te maken waarvan beide de methoden extenden:
Code (php)
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
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
<?php
class Person
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->rang = 10;
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
}
class Admin extend Person
{
// ...
}
class User extend Person
{
// ...
}
?>
class Person
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->rang = 10;
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
}
class Admin extend Person
{
// ...
}
class User extend Person
{
// ...
}
?>
Alleen nu hebben we een probleem. Hoe gaan we het nu oplossen met de rangen? Nu hebben ze beide 10 als rang, terwijl de User rang 1 moet hebben.
De oplossing is werken met een abstracte klasse. Hiermee kun je sommige methoden vaststellen (zoals getName, getRang) en andere open laten, die moet de subklasse invullen. Deze methoden zijn de abstracte methoden. Het mooie daarvan is dat we verplichten dat de subklasse die methode moet hebben, maar wat hij doet dat gaat de superklasse niet aan.
Om deze code nog wat meer flexibel te maken gaan we de laatste duplicated code (de constructor) in 2 stukken splitsen: Het setten van de naam en het setten van de rang.
Het setten van de naam doen we in de constructor, alleen het setten van de rang doen we in een private method setRang. Hierdoor kunnen we in de abstracte superklasse alle dubbele code overnemen uit de subklassen en hebben we in de subklassen alleen unieke code:
Code (php)
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
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
<?php
class Person
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->setRang(); // roep onze, door de subklasse aangemaakte, setRang method aan
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
// laat deze method invullen door de subklasse
abstract private function setRang();
}
class Admin extend Person
{
// voeg rang = 10 toe
private function setRang()
{
$this->rang = 10;
}
}
class User extend Person
{
// voeg rang = 1 toe
private function setRang()
{
$this->rang = 1;
}
}
?>
class Person
{
protected $name;
protected $rang;
public function __construct($name)
{
$this->name = $name;
$this->setRang(); // roep onze, door de subklasse aangemaakte, setRang method aan
}
public function getName()
{
return $this->name;
}
public function getRang()
{
return $this->rang;
}
// laat deze method invullen door de subklasse
abstract private function setRang();
}
class Admin extend Person
{
// voeg rang = 10 toe
private function setRang()
{
$this->rang = 10;
}
}
class User extend Person
{
// voeg rang = 1 toe
private function setRang()
{
$this->rang = 1;
}
}
?>
maar het databaseConnect kan databaseConfig wel extenden toch omdat het de info daaruit gebruikt om een verbinding op te zetten. denk ik nu zo goed?
ff wat ik tot nu toe heb voor deze 2 class. http://pastebin.com/qZTDL7ut
@jeroen ja ik dacht wat als ik nu de configuratie nodig heb voor een andere sql type bijv mysqli of pdo. dan moet ik de configuratie kunnen gebruiken in die classes toch? hoef ik niet weer alles erin te tikken. of geld dit hier niet voor op een of ander manier?
Gewijzigd op 10/06/2012 21:55:02 door Reshad F
Een abstracte class kan zoals Terence al zegt abstracte methodes bevatten. Belangrijker echter is dat een abstracte class niet geinstantieerd kan worden, je kan de abstracte class zelf dus niet gebruiken, je kan er alleen een afgeleide class op schrijven die de eventuele abstracte functies erin implementeert en verder uitbreid.
Het nut van een abstracte class is dat je bepaalde functionaliteit die je in verschillende classes wil inbouwen al kan definieren en dus kan gebruiken op meerdere plekken. De abstracte functies erin kan je dan weer gebruiken om af te dwingen dat bepaalde functies zullen worden geimplementeerd door de afgeleide classes. Als je dat namelijk op die manier afdwingt, kan je functies in de abstracte class al aanroepen, zonder dat je weet hoe het geimplementeerd gaat worden.
En voor de duidelijkheid, een abstracte class kan abstracte functies bevatten, maar hoeft niet. Als echter een class een abstracte functie bevat, dan moet de class abstract zijn.
Edit: beetje mosterd na de maaltijd. Ik zie nu pas de hele lap tekst van Wouter :-)
Gewijzigd op 10/06/2012 21:37:09 door Erwin H
@Erwin ik snap het helemaal. alles wat je verwacht te gebruiken ( zoals een rang setten dan weet je dat je een abstracte class gaat gebruiken) en voor bijv de auto klasse een fuel abstracte ( gaat er bijv in een auto diesel in en in de andere petrol )
maar bijv in een database connection classe die ik nu aan het maken ben kan ik dus absoluut geen abstractie gebruiken toch? want er is maar 1 manier om te connecten. maar ik denk nu dus bij mezelf. nee het kan wel. want je hebt verschillende databases dus dan kan je daar een abstractie voor maken. denk ik nu zo goed of..?
Even een vraag voor je: Probeer eens een goed overzicht te tekenen van deze klassen. Welke is abstract en welke niet en welke extends welke?
Merk overigens op dat je ook nog een interface hebt. Hiermee kan je klassen aan elkaar koppelen. Je weet dan eigenlijk nog geen 1 methode hoe je ze moet afhandelen, alleen je weet wel dat je straks zeker wilt zijn dat klassen bepaalde methoden bevatten. Later kun je in andere klassen dan kijken of ze afstammen van die Interface, waarmee je zeker weet welke methoden ze bevatten. Wat die methoden precies doen boeit je dan niks, als ze maar doen wat ze moeten doen.
En dan ga je komen naar 1 van de ontwerpprincipes: Programmeer naar een interface/superklasse i.p.v. naar een implementatie/subklasse. Probeer dus altijd superklassen te vinden en probeer zo min mogelijk over te laten aan de subklassen.
wouter ik heb het even op papier gezet (digitaal dan) :p
link
is de denkwijze voor deze klasse zo goed?
Gewijzigd op 10/06/2012 22:14:20 door Reshad F
Wat mijns inziens zinvoller is, is om na te denken over wat voor soort database te gaat gebruiken. Op het moment gebruik je waarschijnlijk MySQL, maar bij een volgend project is dat misschien MSSQL, of nog later Oracle of DB2.
Dan wordt het van belang om ervoor te zorgen dat je verschillende connectie objecten kan hebben voor de verschillende database systemen. De rest van je classes zal het worst zijn (met een kleine slag om de arm voor specifieke SQL verschillen), die willen gewoon een connectie kunnen maken en queries kunnen uitvoeren.
Wat je dan kan doen is een interface schrijven die bepaalt welke methodes de verschillende connection classes moeten implementeren. Zo kan je ervoor zorgen dat je andere database classes altijd de connection class kan aanspreken, voor welke DB het ook is.
Gewijzigd op 10/06/2012 22:21:39 door Erwin H
.... implements ... weet niet of dat het is?
edit: heb het even uitgevogelt op php.net dat is het inderdaad. maar hoe ziet zown implement er dan uit wanneer de database verschilt?
Gewijzigd op 10/06/2012 22:32:53 door Reshad F
Ook moet je even goed gaan nadenken. De dbConfig klasse slaat waardes op als user, pass, dbname, host, enz. Verschillen die per adapter?
En het maken van de verbinding in dbConnect, verschillen die per adapter?
Welke klasse moet dus meerdere subklassen hebben en welke niet?
En nu wil ik nog even wat structuur aanbrengen in je tekeningen, aangezien dat heel belangrijk gaat worden. Dit doen we namelijk allemaal met UML diagrammen. Een voorbeeld tekening van mijn voorbeeld hierboven kun je hier vinden. Wat uitleg:
- Blokken bevatten eerst de klassenaam, is dit een abstracte klasse dan is deze cursief.
- Daarna komt de sectie met properties en eventuele types, vooraan staat de access (+ is public, # protected en - private).
- Dan de method met eventuele argumenten en access, abstracte methods zijn cursief
- een doorgetrokken lijn met een open hoek aan het eind is een IS_EEN (extend) relatie tussen klassen.
- een stippellijn met een open hoek aan het eind is een IMPLEMENTEERT (met interfaces) relatie.
- een doorgetrokken lijn met 2 streepjes als een hoek aan het eind is een HEEFT_EEN relatie.
Gewijzigd op 10/06/2012 22:46:46 door Wouter J
- databaseConnect apart van de databaseConfig. maar het moet niet extend worden. terwijl de een de ander wel nodig heeft.
dit kan ik gewoon oproepen dmv de constructor. >> __construct(databaseconfig)
- dbconfig slaat waardes op maar dit zijn volgens mij voor elke database gelijk of het per adapter verschilt. ik denk het niet?
staan ze zo dan goed in hun eigen klasse of moeten ze dan op een andere manier verwerkt worden?
- het maken van een verbinding is volgens mij per adapter wel verschillend maar dit weet ik niet omdat ik tot op heden alleen met mysql gewerkt heb dus ik weet niet hoe het bij andere databases gaat en of ik daar dan dezelfde manier voor kan gebruiken.
al met al ben ik zover gekomen met de code afgeleid van wat ik op papier heb staan. --> http://pastebin.com/uXr6Y5Md
pak ik het zo goed aan?
Zou je niet beter Database als Class kunnen gebruiken. En er is een regel dat een class altijd met een hoofdletter moet beginnen.
Ook doet Reshad het denken in objecten nu goed, qua connect apart. Je kan ook een Database klasse maken, maar dan heb je het niet meer over objecten maar over een verzameling functies. Ook handig soms, alleen Reshad wil nu OO leren.
Reshad, nee je bent niet goed bezig. Voor de helft zit je goed, voor de andere helft niet. De Configuratie verschilt niet per adapter, dat is dus 1 config klasse.
De connection verschilt wel per adapter. Je maakt dus 1 superklasse die alle methods pakt die gelijk zijn bij alle adapters. Vervolgens heb je subklassen die speciaal per adapter zijn. Je krijgt dus zoiets:
Code (php)
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
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
Reshad F op 10/06/2012 22:26:55:
hmm heb je hier een klein voorbeeldje van misschien erwin? hoe de interface er uit hoort te zien. dit ben ik nog niet tegengekomen in tutorials tot nu toe. althans dat denk ik.. ben wel dingen tegengekomen met
Bedenk wat een config class zou moeten kunnen. De config class moet de verbinding met de database maken, de verbinding sluiten, een query uitvoeren op de database, uitkomsten terugsturen etc. Op basis daarvan kan je een interface opzetten om ervoor te zorgen dat elke class die zich wil voordoen als connectie class dit kan:
Dit is uiteraard een voorbeeld, in het echt zal je meer methodes nodig hebben en wil je misschien in de executeQuery methode al de resultaten teruggeven.
Code (php)
Vervolgens kan je een object deze interface laten implementeren:
Code (php)
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
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
<?php
class MySQL_Connection_Class implements Connection_Interface{
private $host;
private $username;
private $password;
public function __construct( $host, $username, $password ){
$this->host = $host;
$this->username = $username;
$this->password = $password;
}
public function connect(){
//make the connection
}
public function isConnected(){
//check if there is a connection, return a boolean
}
public function disconnect(){
//close the db connection
}
public function executeQuery( $sql ){
//execute the given query
}
public function getResults(){
//return the results from the last executed query
}
}
?>
class MySQL_Connection_Class implements Connection_Interface{
private $host;
private $username;
private $password;
public function __construct( $host, $username, $password ){
$this->host = $host;
$this->username = $username;
$this->password = $password;
}
public function connect(){
//make the connection
}
public function isConnected(){
//check if there is a connection, return a boolean
}
public function disconnect(){
//close the db connection
}
public function executeQuery( $sql ){
//execute the given query
}
public function getResults(){
//return the results from the last executed query
}
}
?>
Als je nu vervolgens een database class hebt die voor de rest van de applicatie alle database interactie verzorgt, dan kan je daarin een connection setter maken waarbij je via typehinting afdwingt dat het connection object ook echt de juiste interface implementeert.
Code (php)
Als je nu in die setter een object meegeeft dat de Connection_Interface niet implementeert, dan zal je een runtime error krijgen.
Gewijzigd op 11/06/2012 09:50:05 door Erwin H
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
vervolgens maak ik een class die deze implementeert. dat wordt dan de databaseConnect iets zoals.
Code (php)
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
class databaseConnect implements Connection_Interface
{
/**
* declareren van alle functies
*/
private $hostname;
private $username;
private $pass;
private $database;
private $table;
private $connector;
public function __construct($hostname, $username, $pass, $database, $table, $connector)
{
$this->databaseConfig = $databaseConfig;
}
public function __destruct()
{
#destroy function
}
// open connectie
public function openConnect()
{
try
{
if($this->databaseConfig->connector == "mysql")
{
$this->openConnect = mysql_connect($this->databaseConfig->hostname, $this->databaseConfig->username, $this->databaseConfig->pass);
$this->databaseConnect = mysql_select_db($this->databaseConfig->database);
}
}
catch (Exception $e)
{
return $e;
}
}
#run een query
public function startQuery($runQuery)
{
}
#sluit connectie
public function closeConnect()
{
}
/*
* ping server om te zien of deze open verbinding heeft.
*/
public function pingServer()
{
}
public function getResults()
{
}
}
?>
class databaseConnect implements Connection_Interface
{
/**
* declareren van alle functies
*/
private $hostname;
private $username;
private $pass;
private $database;
private $table;
private $connector;
public function __construct($hostname, $username, $pass, $database, $table, $connector)
{
$this->databaseConfig = $databaseConfig;
}
public function __destruct()
{
#destroy function
}
// open connectie
public function openConnect()
{
try
{
if($this->databaseConfig->connector == "mysql")
{
$this->openConnect = mysql_connect($this->databaseConfig->hostname, $this->databaseConfig->username, $this->databaseConfig->pass);
$this->databaseConnect = mysql_select_db($this->databaseConfig->database);
}
}
catch (Exception $e)
{
return $e;
}
}
#run een query
public function startQuery($runQuery)
{
}
#sluit connectie
public function closeConnect()
{
}
/*
* ping server om te zien of deze open verbinding heeft.
*/
public function pingServer()
{
}
public function getResults()
{
}
}
?>
ga ik zo de goede kant op hiermee?
Toevoeging op 11/06/2012 10:39:19:
en hier even hoe ik de volledige code heb met deze gedachte van die implement ( het is natuurlijk niet goed nog ) maar even om te zien hoe ik het heb.
http://pastebin.com/xZ9E0jML
Gewijzigd op 11/06/2012 10:37:06 door Reshad F
if($this->databaseConfig->connector == "mysql")
Het hele idee van het op deze manier opbouwen is dat je de verschillende databases (en verschillende technische methodes om ze aan te roepen) scheidt in verschillende connectie classes. Dus je hebt een class voor mysql, je hebt een class voor DB2, voor Oracle etc. En als je mysql_, mysqli_ en pdo wilt gebruiken heb je daar ook ieder een class voor. Binnen de connectie class gebruik je dus altijd maar 1 van die technieken en hoef je dus nooit te checken welke je gebruikt.
nu heb ik een klein vraagje. ik heb nu dus
Code (php)
maar wat is nu die class databaseConfig in dit geval? hoort dit een abstract class te zijn? want de gegevens ervan ( om een verbinding tot stand te brengen ) wordt pas later gegeven toch? en blijft dit dan een standalone class of moet deze extend worden?
Gewijzigd op 11/06/2012 11:44:03 door Reshad F
Of je het moet extenden of niet is geen antwoord op te geven. Dat ligt totaal aan hoe je er verder mee wilt werken.