Inheritance
Omdat ik deze situatie nogal vaak tegenkom heb ik besloten toch maar te vragen hoe dit nou precies zit:
Wanneer gebruik je inheritance en wanneer geef je informatie mee via bijv. de constructor?
Ik snap dat deze vraag nogal onduidelijk is dus probeer ik het wat te verduidelijken via onderstaande pseudo code.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
class Gem
{
private String color = "red";
private Event onPickUp;
public Gem(String color, Event onPickUp)
{
this.color = color;
this.onPickUp = onPickUp;
}
}
Gem gem_1 = new Gem("red", Event.PlusOnePoint);
Gem gem_2 = new Gem("blue", Event.PlusTenPoints);
{
private String color = "red";
private Event onPickUp;
public Gem(String color, Event onPickUp)
{
this.color = color;
this.onPickUp = onPickUp;
}
}
Gem gem_1 = new Gem("red", Event.PlusOnePoint);
Gem gem_2 = new Gem("blue", Event.PlusTenPoints);
In bovenstaande voorbeeld is er dus 1 class en vanuit deze class kun je meerdere edelstenen aanmaken, elke edelsteen heeft een andere kleur en een andere callback.
Maar wat ook mogelijk is, is het volgende:
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
class abstract Gem
{
public Gem()
{
}
public void onPickUp()
{
}
}
class RedGem extends Gem
{
public void onPickUp()
{
points++;
}
}
class BlueGem extends Gem
{
public void onPickUp()
{
points += 10;
}
}
Gem gem_1 = new RedGem();
Gem gem_2 = new BlueGem();
{
public Gem()
{
}
public void onPickUp()
{
}
}
class RedGem extends Gem
{
public void onPickUp()
{
points++;
}
}
class BlueGem extends Gem
{
public void onPickUp()
{
points += 10;
}
}
Gem gem_1 = new RedGem();
Gem gem_2 = new BlueGem();
Het voorbeeld is misschien een beetje simpel, maar ik hoop dat jullie mijn bedoeling snappen.
Alvast bedankt voor de hulp!
Gewijzigd op 15/02/2016 18:04:08 door Lord Gaga
Informatie meegeven aan een functie (Een constructor is ook een functie) gebruik je veel, het liefst zo veel mogelijk. Stel dat je een User object hebt en data uit dit object voor de output in de view wilt gebruiken. Je geeft dan het User object gewoon mee aan de view. Als je ook nog even een email wilt genereren waarin wat data moet komen te staan dat uit het User object komt dan geef je dus het User object ook gewoon even door aan je Mailer class.
Wel moet je even letten waarvoor een constructor bedoeld is. Initialisatie van je (te instantiëren) object. De properties voorzien van een waarde dus.
Wat hier op aansluit is Dependency Injection. Zeker de moeite waard om te lezen.
In het algemeen mag je aannemen dat je bij data objecten (Gem in jouw geval) geen inheritance moet gebruiken. Data objecten bevatten namelijk alleen data, geen logic. Inheritance kun je het beste alleen toepassen in gevallen dat gedrag van een object veranderd. Omdat data objecten geen gedrag hebben kunnen ze deze ook niet anders hebben dat hun parent/child.
Als we naar je tweede voorbeeld gaan zien we dat er een duidelijk probleem ontstaat: Onze data objecten bevatten logica. Dit maakt het geheel veel moeilijker. Stel nu bijv. dat we moeilijkere criteria krijgen voor het opwaarderen van punten (bijv. mag alleen +10 als de slijper zijn werk goed heeft gedaan, mag alleen als de huidige valuta boven een bepaalde waarde zit, etc.). Je kan je indenken dat ons mooie data object ineens een bende van logica gaat worden.
Je kunt deze logica dus het beste opsplitsen naar een andere class, bijv. GemPointsCalculator:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface GemPointsCalculator
{
public int pointsForGem(Gem gem);
}
class SimpleGemPointsCalculator implements GemPointsCalculator
{
public int pointsForGem(Gem gem)
{
switch (gem.color()) {
case 'red':
return gem.points() + 1;
break;
case 'blue':
return gem.points() + 10;
break;
}
}
}
{
public int pointsForGem(Gem gem);
}
class SimpleGemPointsCalculator implements GemPointsCalculator
{
public int pointsForGem(Gem gem)
{
switch (gem.color()) {
case 'red':
return gem.points() + 1;
break;
case 'blue':
return gem.points() + 10;
break;
}
}
}
Nu gaat deze class op gegeven moment ook veel logica bevatten. In dat geval moet je het waarschijnlijk nog meer opsplitsen, waardoor elke Gem zijn eigen GemPointsCalculator krijgt en een algemene calculator die deze dan kan aanroepen.
Ik denk dat ik het een beetje beter begrijp dan voorheen. :P
In dit geval was de Gem class een simpel voorbeeld.
Waar ik in de praktijk mee bezig ben zijn enkele JavaScript spelletjes, en deze spelletjes bevatten natuurlijk elk onderdelen die in elk spelletje aanwezig zijn:
- De mainloop (voor game logic en renderen van entities)
- Scenes (voor menu's, verschillende levels, etc.)
- Entities (de objecten in de spelletjes)
Op dit moment twijfel ik dus over de 2 aanpakken in mijn openingspost, maak ik gebruik van:
of toch van inheritance (pseudocode):
En hoe doe ik dit met de andere objecten? (Scene, Entity, AssetLoader, etc.)
Wanneer ik naar verschillende JavaScript frameworks kijk, zie ik dat vooral de eerste manier vaak wordt toegepast, maar toch twijfel ik. (Ik weet dat ik beter gebruik kan maken van een bestaand framework maar ik wil het toch onder de knie krijgen dit zelf te kunnen.)