Waarom OO?!
Pagina: « vorige 1 2 3 volgende »
Reshad F op 28/11/2013 00:39:19:
Hier geef ik je de tip om wanneer mogelijk een interface te gebruiken omdat je waar mogelijk moet programmeren naar een interface en niet naar een implementatie toe.
Reshad, waarom is dit? Kun je dat eens uitleggen? Mij lijkt het juist handig om een abstracte class te gebruiken omdat de constructor en class property telkens hetzelfde zijn. Het enige dat verschilt is de validate functie. De rest is idem. Is dan een abstracte class niet handiger?
Ozzie PHP op 28/11/2013 01:19:08:
Mij lijkt het juist handig om een abstracte class te gebruiken omdat de constructor en class property telkens hetzelfde zijn. Het enige dat verschilt is de validate functie. De rest is idem. Is dan een abstracte class niet handiger?
Hier hoort validate() juist in de interface. De interface schrijft voor dat alle implementaties de methode validate() moeten hebben, maar niet hoe die wordt ingevuld.
Een abstracte klasse gebruik je vooral voor gedeelde of gemeenschappelijke standaardcode. Dat is in dit geval dan niet validate(), want die methode verschilt per klasse.
Als jij type hint op een abstract class, dan MOET het een instantie van een child van die class zijn.
Als jij type hint op een interface, dan kan het een instantie van elke class zijn ZOLANG het die interface maar implementeert.
In beide gevallen zal het werken.
Dat doet een abstract class toch ook? De hele class is verder precies hetzelfde, alleen de validate method verschilt per class, dus die maak je dan abstract. Als je een interface gebruikt ga je telkens dezelfde code opnieuw schrijven, terwijl je als een abstracte class gebruikt de childs alleen maar een validate method hoeven te hebben. Daar is een abstract class toch voor bedoeld? Als je gebruikmaakt van een interface, dan maakt het niet uit hoe de classes eruit zien, zolang ze maar de methods uit de interface ondersteunen. Bij een abstract class is de class feitelijk al klaar, alleen de abstracte methods, in dit geval de validate method, moeten nog worden ingevuld door de childs. Dat is dan toch een logischere oplossing dan een interface in dit geval?
Je kunt inderdaad in de abstract class een validate() opnemen die door child classes mag worden overschreven. Maar dan is onduidelijk of dat ook moet. Dat kun je enkel in de interface afdwingen.
http://php.net/manual/en/language.oop5.abstract.php
Quote:
Methods defined as abstract simply declare the method's signature - they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child.
Hier staat dat abstracte methods juist geen functionaliteit mogen bezitten en moeten worden ingevuld door de child class. Dat lijkt niet overeen te komen met wat jij zegt "Nee, in een abstract class zijn de methoden vaak al gebruiksklaar.", of ik begrijp je verkeerd.
Even een voorbeeld. We hebben "een betaalmethode", maakt niet uit welke. Daarvoor hebben we altijd een te betalen bedrag $Amount nodig. En aangezien we sowieso nu al een bedrag hebben, voegen we voor de volledigheid een getAmount() toe:
Code (php)
Dat bedrag moet er nog in als we een object maken. Alleen hanteren verschillende betaalmethoden verschillende bedragen. De verplichte aanwezigheid van een setter setAmount() formaliseren we in de interface:
Hiermee hebben we een prototype dat altijd een bedrag $Amount heeft, dat altijd met de universele getAmount() kan worden opgevraagd en dat altijd met een unieke setAmount() moet worden ingesteld. Daarmee kunnen we tot slot verschillende betaalsystemen ondersteunen, bijvoorbeeld iDEAL voor bedragen van € 0,84 t/m € 10.000 en MrCash voor bedragen van € 0,49 t/m € 5.000:
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
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 iDEALPayment extends AbstractPayment
{
public function setAmount($amount)
{
if (is_int($amount) && $amount >= 84 && $amount <= 1000000) {
$this->Amount = $amount;
} else {
throw new Exception('Invalid iDEAL amount.');
}
return $this;
}
}
class MrCashPayment extends AbstractPayment
{
public function setAmount($amount)
{
if (is_int($amount) && $amount >= 49 && $amount <= 500000) {
$this->Amount = $amount;
} else {
throw new Exception('Invalid MrCash amount.');
}
return $this;
}
}
?>
class iDEALPayment extends AbstractPayment
{
public function setAmount($amount)
{
if (is_int($amount) && $amount >= 84 && $amount <= 1000000) {
$this->Amount = $amount;
} else {
throw new Exception('Invalid iDEAL amount.');
}
return $this;
}
}
class MrCashPayment extends AbstractPayment
{
public function setAmount($amount)
{
if (is_int($amount) && $amount >= 49 && $amount <= 500000) {
$this->Amount = $amount;
} else {
throw new Exception('Invalid MrCash amount.');
}
return $this;
}
}
?>
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
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
<?php
interface Moveable
{
function goTo(Loation $location);
}
interface Location
{
public getLattitude();
}
abstract class Car implements Moveable
{
abstract function goTo(Location $destination);
abstract function startEngine();
}
class Bike implements Moveable
{
public goTo(Location $location)
{
...
}
}
function driveTo(Car $car, Location $location)
{
return $car->goTo($location);
}
function goTo(Moveable $object, Location $location)
{
$object->moveTo($location);
}
driveTo(new Bike, new Home); // werkt niet
goTo(new Bike, new Home); // werkt wel
?>
interface Moveable
{
function goTo(Loation $location);
}
interface Location
{
public getLattitude();
}
abstract class Car implements Moveable
{
abstract function goTo(Location $destination);
abstract function startEngine();
}
class Bike implements Moveable
{
public goTo(Location $location)
{
...
}
}
function driveTo(Car $car, Location $location)
{
return $car->goTo($location);
}
function goTo(Moveable $object, Location $location)
{
$object->moveTo($location);
}
driveTo(new Bike, new Home); // werkt niet
goTo(new Bike, new Home); // werkt wel
?>
Interfaces zijn flexibeler. Interfaces zijn meer future proof. Als ik was begonnen met een abstract Car class zonder interfaces, en later blijk een class Bike ook nodig, dan zal ik moeten refactoren. Als ik meteen een interface had gebruikt hoefde ik alleen maar de classe Bike aan te maken.
Gewijzigd op 28/11/2013 12:40:38 door Dos Moonen
@Wouter: duidelijk voorbeeld! Ik snap alleen niet waarom je dan de setAmount method niet als abstracte method opneemt in de abstract class:
Code (php)
Wat is het verschil met een interface? Ik grijp weer even terug naar de uitleg op php.net
"all methods marked abstract in the parent's class declaration must be defined by the child"
Ofwel, de abstracte methods in de parent class (in jouw voorbeeld setAmount) moeten worden ingevuld door de child class. Wat is nu het verschil met een interface? :-s
Interface methods zijn ook altijd impliciet abstract methods.
Abstract methods kunnen public of protected zijn, private kan niet.
Methods die in een child/interface al als abstract gedefineerd zijn hoef je in een abstract class niet nogmaals te defineren. Het kan wel, zolang het maar precies de zelfde signature heeft.
"all methods marked abstract in the parent's class declaration must be defined by the child" geldt voor classes die niet abstract zijn.
Dos, laat ik m'n vraag anders stellen. Waarom wordt in het voorbeeld met phonenumber gesteld dat je beter een interface kunt gebruiken dan een abstracte class? Als de child class de abstract class extends dan is de child class al zo goed als klaar. Dan hoef je daar alleen de validate method in te zetten. Terwijl als je geen gebruik maakt van een abstract class en de child class een interface laat implementeren, dan moet je de hele class opnieuw opbouwen. Dat verschil snap ik niet. Wanneer maak je een method abstract? Mijn gedachte: wanneer deze method ook moet voorkomen in de child class.
Even een heel simpel voorbeeldje.
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php
interface pizza {
public function prepare();
public function bake();
public function slice();
public function serve();
}
class funghiPizza implements pizza {
private $ingredients;
public function __construct($ingredients) {
// minimaal een paar ingredienten meegeven want van alleen 1 ingredient kunnen we geen pizza maken. :p
if(is_array($ingredients)){
foreach ($ingredients as $ingredient) {
$this->ingredients[] = $ingredient;
}
return $this->ingredients;
}
else {
throw new InvalidArgumentException('No error given: ' . $ingredients);
return false;
}
}
public function prepare() {
$this->gooihetindeblender();
}
public function bake() {
// etc
}
public function slice() {
// etc
}
public function serve() {
// etc
}
}
abstract class pizza {
public function __construct($ingredients) {
// minimaal een paar ingredienten meegeven want van alleen 1 ingredient kunnen we geen pizza maken. :p
if(is_array($ingredients)){
foreach ($ingredients as $ingredient) {
$this->ingredients[] = $ingredient;
}
return $this->ingredients;
}
else {
throw new InvalidArgumentException('No error given: ' . $ingredients);
return false;
}
}
public function prepare() {
$this->gooiErEierenIn();
}
public abstract function bake();
public abstract function slice();
public abstract function serve();
}
class rarePizza extends pizza {
public function __construct() {
parent::__construct();
}
public function bake() {
}
public function slice() {
}
public function serve() {
}
}
?>
interface pizza {
public function prepare();
public function bake();
public function slice();
public function serve();
}
class funghiPizza implements pizza {
private $ingredients;
public function __construct($ingredients) {
// minimaal een paar ingredienten meegeven want van alleen 1 ingredient kunnen we geen pizza maken. :p
if(is_array($ingredients)){
foreach ($ingredients as $ingredient) {
$this->ingredients[] = $ingredient;
}
return $this->ingredients;
}
else {
throw new InvalidArgumentException('No error given: ' . $ingredients);
return false;
}
}
public function prepare() {
$this->gooihetindeblender();
}
public function bake() {
// etc
}
public function slice() {
// etc
}
public function serve() {
// etc
}
}
abstract class pizza {
public function __construct($ingredients) {
// minimaal een paar ingredienten meegeven want van alleen 1 ingredient kunnen we geen pizza maken. :p
if(is_array($ingredients)){
foreach ($ingredients as $ingredient) {
$this->ingredients[] = $ingredient;
}
return $this->ingredients;
}
else {
throw new InvalidArgumentException('No error given: ' . $ingredients);
return false;
}
}
public function prepare() {
$this->gooiErEierenIn();
}
public abstract function bake();
public abstract function slice();
public abstract function serve();
}
class rarePizza extends pizza {
public function __construct() {
parent::__construct();
}
public function bake() {
}
public function slice() {
}
public function serve() {
}
}
?>
Zoals je hier zit kan ik bij de interface welke pizza dan ook maken en alles gebruiken en doen of laten wat ik ermee wil. Bij de abstracte klasse wordt dit al gauw beperkt omdat in dit geval de prepare methode bepaalt hoe een pizza gemaakt wordt en naar mijn idee hoort een pizza anders gemaakt te worden dan een andere pizza. Het gebruik van interfaces is beter dan een abstracte klasse te gebruiken omdat je meer controle hebt over wat een child klasse moet doen.
Hier wil ik niet mee zeggen dat een abstracte klasse nu de boeman is en niet gebruikt moet worden want ook deze heeft zijn voordelen maar dat hangt af per geval. Een abstrace klasse zou ik eerder gebruiken wanneer ik bijvoorbeeld een klasse person maak en dan een klasse Werknemer die bijv setName, getName etc moet overerven
Toevoeging op 28/11/2013 13:33:40:
Ozzie PHP op 28/11/2013 13:09:53:
Dos, laat ik m'n vraag anders stellen. Waarom wordt in het voorbeeld met phonenumber gesteld dat je beter een interface kunt gebruiken dan een abstracte class? Als de child class de abstract class extends dan is de child class al zo goed als klaar. Dan hoef je daar alleen de validate method in te zetten. Terwijl als je geen gebruik maakt van een abstract class en de child class een interface laat implementeren, dan moet je de hele class opnieuw opbouwen. Dat verschil snap ik niet. Wanneer maak je een method abstract? Mijn gedachte: wanneer deze method ook moet voorkomen in de child class.
Moet het bij een interface dan niet voorkomen in de childklasse? ( Jazeker wel ) alleen is de vrijheid die je hierbij krijgt dat jij het aan de child overlaat hoe hij/zij deze invult en niet de implementatie klakkeloos overneemt zie mijn voorbeeld hierboven.
Fatal error: Can't inherit abstract function PaymentInterface::setAmount() (previously declared abstract in AbstractPayment)
Het voorbeeld werkt dan alleen nog als we de interface overboord gooien. Het lijkt daarmee een keuze en dan verkies ik de interface. Je kunt anders helemaal geen interface meer implementeren. Je zit vast aan een extends van de abstracte klasse, ook als je geheel andere klassen wilt bouwen met dezelfde interface.
Op dat laatste komt het aan: de interface formaliseert slechts de API van klassen, een abstracte klasse is al gedeeltelijk een implementatie.
Gewijzigd op 28/11/2013 13:35:45 door Ward van der Put
@Reshad: terug naar jouw pizza voorbeeldje dan. Ik zou zeggen dat iedere pizza in een restaurant op dezelfde manier wordt gebakken, gesliced en geserveerd. Bij het bakken verschilt hooguit het aantal minuten, maar het in stukken snijden en het serveren gebeurt op dezelfde manier. Ik ga er vanuit dat alle pizza's door een ober worden geserveerd en dat niet een pizza margherita ineens door een olifant wordt opgediend. In mijn optiek zijn de methods bake, slice en serve dus onderdeel van de abstract class. Aan de functie prepare moet per pizza een eigen invulling worden gegeven dus die maken we abstract. Dan krijgen we dus dit:
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
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
abstract class pizza {
protected $ingredients;
public function __construct($ingredients) {
// set the ingrediënten als class properties
}
abstract public function prepare(); // de prepare method verschilt per pizza, dus abstract!
public function bake() {
// stop de pizza in de oven en bak 'm
}
public function slice() {
// snij de pizza in stukken
}
public function serve() {
// serveer de pizza
}
}
?>
abstract class pizza {
protected $ingredients;
public function __construct($ingredients) {
// set the ingrediënten als class properties
}
abstract public function prepare(); // de prepare method verschilt per pizza, dus abstract!
public function bake() {
// stop de pizza in de oven en bak 'm
}
public function slice() {
// snij de pizza in stukken
}
public function serve() {
// serveer de pizza
}
}
?>
En dan de pizza zelf:
Code (php)
Dit is toch hoe het zou moeten?????
(P.S. je kunt geen waarden returnen in een constructor zoals jij in je voorbeeld doet)
Toevoeging op 28/11/2013 13:55:39:
Ward van der Put op 28/11/2013 13:35:16:
Je kunt anders helemaal geen interface meer implementeren. Je zit vast aan een extends van de abstracte klasse, ook als je geheel andere klassen wilt bouwen met dezelfde interface.
Maar de vraag is in hoeverre het instellen van een telefoonnummer of een amount beschouw moet worden als een interface, of als onderdeel van de blauwdruk van de class. Dat vind ik nogal lastig te bepalen. Als ik jouw redenatie volg dan zouden er in jouw abstracte classes nooit abstracte methods te vinden zijn.
http://sandbox.onlinephpfunctions.com/code/6fb652c5c012313514cff127b8a556293f69ede4
5.3.3 geeft die foutmelding van jou, 5.3.10 (er zit niets tussen) slikt het wel.
PHP 5.3 is niet meer ondersteund en negeer ik persoonlijk lekker.
http://stackoverflow.com/questions/1913098/what-is-the-difference-between-an-interface-and-abstract-class
Dat is wat ik bedoel. De hele class om het telefoonnummer of de betaling of de pizza in te stellen of maken is er al. Je moet alleen even de gaten opvullen, ofwel die ene method: setPhoneNumber, setAmount, of preparePizza.
Op deze link: Quote:
Abstract classes look a lot like interfaces, but they have something more : you can define a behavior for them. It's more about a guy saying "these classes should look like that, and they got that in common, so fill in the blanks!".
Dat is wat ik bedoel. De hele class om het telefoonnummer of de betaling of de pizza in te stellen of maken is er al. Je moet alleen even de gaten opvullen, ofwel die ene method: setPhoneNumber, setAmount, of preparePizza.
Dos Moonen op 28/11/2013 13:57:26:
Oh, fijn is dat. Was dat een ordinaire bug? Of meer een ongelukkige keuze voor de gelijktijdige implementatie van een abstracte klasse en een interface?@ward Het werkt prima op ondersteunde PHP versies:
http://sandbox.onlinephpfunctions.com/code/6fb652c5c012313514cff127b8a556293f69ede4
5.3.3 geeft die foutmelding van jou, 5.3.10 (er zit niets tussen) slikt het wel.
PHP 5.3 is niet meer ondersteund en negeer ik persoonlijk lekker.
http://sandbox.onlinephpfunctions.com/code/6fb652c5c012313514cff127b8a556293f69ede4
5.3.3 geeft die foutmelding van jou, 5.3.10 (er zit niets tussen) slikt het wel.
PHP 5.3 is niet meer ondersteund en negeer ik persoonlijk lekker.
Ward van der Put op 28/11/2013 14:03:44:
Dos Moonen op 28/11/2013 13:57:26:
Oh, fijn is dat. Was dat een ordinaire bug? Of meer een ongelukkige keuze voor de gelijktijdige implementatie van een abstracte klasse en een interface?@ward Het werkt prima op ondersteunde PHP versies:
http://sandbox.onlinephpfunctions.com/code/6fb652c5c012313514cff127b8a556293f69ede4
5.3.3 geeft die foutmelding van jou, 5.3.10 (er zit niets tussen) slikt het wel.
PHP 5.3 is niet meer ondersteund en negeer ik persoonlijk lekker.
http://sandbox.onlinephpfunctions.com/code/6fb652c5c012313514cff127b8a556293f69ede4
5.3.3 geeft die foutmelding van jou, 5.3.10 (er zit niets tussen) slikt het wel.
PHP 5.3 is niet meer ondersteund en negeer ik persoonlijk lekker.
Niet verder gezocht dan dat. Ik vermoed een ongelukkige keuze.
Kunnen jullie aub nog reageren op mijn 2 voorgaande berichten?
Ozzie PHP op 28/11/2013 13:52:54:
@Reshad: terug naar jouw pizza voorbeeldje dan. Ik zou zeggen dat iedere pizza in een restaurant op dezelfde manier wordt gebakken,
Nee, je kan bijvoorbeeld denken aan de pizza's in de pizzahut. Daar heb je steen oven pizza's en pan pizza's. Niet hetzelfde dus, gaan niet door hetzelfde proces.
Ozzie PHP op 28/11/2013 13:52:54:
gesliced
Nee, een pizza calzone snijdt je niet in stukken, die serveer je als geheel.
Ozzie PHP op 28/11/2013 13:52:54:
en geserveerd.
Nee, een afhaal pizza wordt niet geserveerd, maar in een doos gepropt en afgegeven.
Met andere woorden, hier wil je een interface gebruiken, omdat alle pizza's in essentie dezelfde stappen doorlopen, maar zeker niet op dezelfde manier. Een absbtracte klasse heeft dus geen toegevoegde waarde.
Gewijzigd op 28/11/2013 14:37:51 door Erwin H