[OOP]communicatie tussen klassen

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • doeternietoe
  • Registratie: November 2004
  • Laatst online: 08:55
Al geruime tijd houdt ik me (hobbymatig) bezig met programmeren. Omdat ik oorspronkelijk begonnen ben met het maken van websites en daarbij gebruik heb gemaakt van PHP doe ik nog steeds het meeste van deze taal, hoewel ik ook wel basale kennis heb van Java, c++ en c. (stel je er niet té veel van voor...) De PHP is OOP en de laatste tijd loop twijfel ik een beetje wat de goede aanpak is in volgende probleem:

Stel, we hebben drie klassen:
classA, classB, classC

Verder hebben we: objectA als instance van classA. objectA heeft als property obectB als instance van classB en objectC als instance van classC.

Of, anders gezegd.
PHP:
1
2
3
4
5
6
7
8
9
10
11
class classA{
  public $objectB, $objectC;
  public function __construct(){
    $this->objectB = new classA();
    $this->objectC = new classB();
  }
}
class classB{}
class classC{}

$objectA = new classA();


Nu kan het voorkomen dat objectB iets tegen objectC wil zeggen. Andersgezegd, objectB wil een public method van objectC aanroepen. Nu is het de vraag hoe deze objecten toegang tot elkaar krijgen.

Hiervoor heb ik een aantal oplossingen gevonden:

- gebruik van globals
Als we $objectA tot een global maken en het property van de classA public, kunnen we overal toegang krijgen tot zowel $objectA en de methods daaruit alsook $objectB en $objectC. Met globals verlies je echter totaal het overzicht over je code. Voor mij valt deze optie dus af. Een voorbeeld:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class classA{
  public $objectB, $objectC;
  public function __construct(){
    $this->objectB = new classA();
    $this->objectC = new classB();
  }
}
class classB{
  public function someMethod(){
    //code
  }
}
class classC{
  public function someOtherMethod(){
    global $objectA;
    //code
    $objectA->objectB->someMethod();
  }
}

global $objectA = new classA();

- Instances doorgeven als argumenten
Bij deze optie worden de instances van classes bijvoorbeeld als argument aan de constructor gegeven. Op die manier heb je er controle over welke classen toegang hebben tot welke objecten. Nadeel is dat je (evenals bij globals) per class een stukje code nodig hebt om deze class toegang te geven tot tot bepaalde objecten. Voor de duidelijkheid een voorbeeld:
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
class classA{
  public $objectB, $objectC;
  public function __construct(){
    $this->objectB = new classA( $this );
    $this->objectC = new classB( $this );
  }
}
class classB{
  public function someMethod(){
    //code
  }
}
class classC{
  private $objectA;
  public function __construct( $objectA ){
    $this->objectA = $objectA;
  }
  public function someOtherMethod(){
    //code
    $this->objectA->objectB->someMethod();
  }
}

$objectA = new classA();

- Het gebruik van static classes
Bij deze laatste oplossing maak je in het geheel geen instance aan van $objectA. Het property van $objectA dat $objectB en $objectC bevat gaat in een static en dat is ook door het gehele script bereikbaar. Een voorbeeld:
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
class classA{
  public static $objectB, $objectC;
  public static function main(){
    self::$objectB = new classA();
    self::$objectC = new classB();
    //code
  }
}
class classB{
  public function someMethod(){
    //code
  }
}
class classC{
  private $objectA;
  public function __construct(){
    //code
  }
  public function someOtherMethod(){
    //code
    classA::$objectB->someMethod();
  }
}

classA::main();


Zelf ben ik de tweede optie wel eens in het werk van derden tegengekomen. Ik heb deze zelf ook vaak gebruikt. De laatste tijd heb ik echter meer de neiging om voor de derde te gaan. Ik heb ook op internet gezocht naar oplossingen die andere mensen voor dit probleem hebben gevonden en vaak wordt dan in forums iets soortgelijks aan het derde aangeraden.

Ik ben wel benieuwd wat de gedachte van GoT hierover is. Bestaan er argumenten waarom één van de opties (en dan voornamelijk uit de laatste twee) beter is? Zie ik een andere optie over het hoofd of heb ik het concept van OOP nog niet helemaal begrepen?

offtopic:
p.s. Er zal hier en daar wel een foutje in de code zitten. Het is nogal lastig om in zo'n GoT berichtvenstertje code te typen zonder alle hulpmiddelen van een fatsoenlijke editor. De bedoeling is dat de ideeën die ik hier neer zet duidelijk zijn.

Acties:
  • 0 Henk 'm!

  • Orphix
  • Registratie: Februari 2000
  • Niet online
De beste optie mis ik: de objecten objectB en objectC doorgeven aan classB en classC. Er is geen reden voor classB en classC om af te weten van het bestaan van classA, als ze enkel met elkaar communiceren. classA is hier de 'lijm' die de twee objecten aan elkaar koppelt. Globals en statics zijn een grote no-no.

Tuurlijk kom je met enkel constructors al snel in een kip-ei verhaal, want voor objectB heb je objectC nodig en omgekeerd, maar dit is op te lossen door dit niet in een constructor mee te geven maar bijvoorbeeld properties te gebruiken.

Daarnaast, een circular dependency zoals je hier aangeeft, waarbij B afhankelijk is van C en C afhankelijk is van B riekt vaak naar een slecht ontwerp. Hoeft niet altijd zo te zijn, maar in heel veel gevallen kan het op een elegantere manier opgelost worden, bijvoorbeeld door het introduceren van een derde class.

Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Waarom heeft class a intern nog een instance van class a (objectB). objectB mag nooit volgens de OO regels nooit direct met objectC communiceren? Immers de koppeling van beide classes liggen buiten hun scope.

class B kan wel events defineren en class A kan vervolgens methods van class C aan de events van B koppelen. Class A gebruikt op dit punt het facade design pattern.

Op het moment dat class B direct met een instance van class C moet communiceren, moet class of zelf een instance van class C initialiseren of je zult een instance van class C via de constructor moeten doorgeven. En bij een circular dependency zou je een 'Initialize' method kunnen introduceren welke een instance van class B en class C krijgt toegewezen. Echter ook hier roept class A dan de Initialize methods aan.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • CodeCaster
  • Registratie: Juni 2003
  • Niet online

CodeCaster

Can I get uhm...

Ik vroeg me dit onlangs af, en heb m'n code met een twintigtal regels verkort en vereenvoudigd door de volgende vier regels:
Java:
1
2
3
4
    public static Settings settings = new Settings();
    public static Logger log = Logger.getLogger(Client.class);
    public static Queue queue = new Queue();
    public static Database database = Database.getInstance();

En nu kan ik vanuit iedere subklasse bijvoorbeeld de volgende code aanroepen:
Java:
1
    Client.log.error("Write unsuccesful");


Misschien is het wel ranzig en niet netjes en druist het volledig tegen de OO-principes in, maar ik vind het in ieder geval overzichtelijker dan dit:
Java:
1
ReadThread rt = new ReadThread(readSocket, settings, log, database);

... en vervolgens voor iedere klasse die een andere klasse nodig heeft een extra argument in z'n constructor meegeven, waarna ik in de klasse zelf ook weer een klasse moet declareren en initialiseren in de constructor met this...

Het niet gebruiken van static lijkt mij nogal veel werk, zeker wanneer je meerdere klassen hebt die door veel andere klassen gebruikt worden (een logklasse, een databaseklasse enzovoorts). Hoe is dit handiger op te lossen dan?

https://oneerlijkewoz.nl
Op papier is hij aan het tekenen, maar in de praktijk...


Acties:
  • 0 Henk 'm!

Verwijderd

Ik weet niet echt wat de "juiste" manieren zijn, maar ik was vandaag in m'n php-boek aan het bladeren en kwam iets over Singleton tegen. Dat heb ik weleens gebruikt in een projectje, hiermee zou dit goed kunnen... Maar of het correct is... Geen idee. ;)

Acties:
  • 0 Henk 'm!

  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Verwijderd schreef op woensdag 31 december 2008 @ 01:15:
Ik weet niet echt wat de "juiste" manieren zijn, maar ik was vandaag in m'n php-boek aan het bladeren en kwam iets over Singleton tegen. Dat heb ik weleens gebruikt in een projectje, hiermee zou dit goed kunnen... Maar of het correct is... Geen idee. ;)
Tja, wat is juist?

Met static is op zich niets mis, behalve dat je wat kracht mist die je met instance methods wel hebt, zoals de mogelijkheden die inheritance biedt.

Aan de andere kant bieden statics wel voordelen qua leesbaarheid op het moment dat je een method heel vaak aanroept, zoals een Logger. Steeds een nieuwe instance creëren is dan overbodig en kan zelfs nadelig uitpakken.

Het voordeel van Singleton t.o.v. 'gewone' statics is in veel gevallen verwaarloosbaar. Zie dit artikel voor wat verschillen: http://twasink.net/blog/a...4/08/singleton_stati.html
CodeCaster schreef op zondag 28 december 2008 @ 15:15:
Ik vroeg me dit onlangs af, en heb m'n code met een twintigtal regels verkort en vereenvoudigd door de volgende vier regels:
Java:
1
2
3
4
    public static Settings settings = new Settings();
    public static Logger log = Logger.getLogger(Client.class);
    public static Queue queue = new Queue();
    public static Database database = Database.getInstance();

En nu kan ik vanuit iedere subklasse bijvoorbeeld de volgende code aanroepen:
Java:
1
    Client.log.error("Write unsuccesful");


Misschien is het wel ranzig en niet netjes en druist het volledig tegen de OO-principes in, maar ik vind het in ieder geval overzichtelijker dan dit:
Java:
1
ReadThread rt = new ReadThread(readSocket, settings, log, database);

... en vervolgens voor iedere klasse die een andere klasse nodig heeft een extra argument in z'n constructor meegeven, waarna ik in de klasse zelf ook weer een klasse moet declareren en initialiseren in de constructor met this...

Het niet gebruiken van static lijkt mij nogal veel werk, zeker wanneer je meerdere klassen hebt die door veel andere klassen gebruikt worden (een logklasse, een databaseklasse enzovoorts). Hoe is dit handiger op te lossen dan?
Het zorgt wel voor minder regels, maar het gaat wel tegen de principes van OO in, want public static fields zijn eigenlijk gewoon globals. En zeker "dure" resources zoals database connections en message queues zou ik niet zomaar in een static gooien.

Ik zie dat je Java gebruikt, in dat geval is het ook belangrijk om te beseffen dat een static een garbage collection root is en dus een mogelijk memory leak kan worden (ja, ik weet het, in theorie heeft Java geen memory leaks, maar in de praktijk kun je als developer prima memory leaks introduceren). Stack variabelen zijn ook GC roots, maar die vallen vanzelf uit scope waardoor ze GC-ed kunnen worden. Statics vallen nooit uit scope, dus tot je ze expliciet op null zet, blijven ze in memory hangen, inclusief al hun referenties.

Bovendien getuigt het niet van net ontwerp, want niemand houdt je tegen om deze database direct vanuit de UI aan te spreken (waar misschien nog geen transactie gestart is) of vanuit een utility mehod die in jouw database klasse gebruikt wordt (waardoor je een circular dependency krijgt).

Ook staan er verschillende zaken bij elkaar die op het eerste gezicht niet bij elkaar horen. Een logger kan ik me voorstellen, aangezien je die vrijwel altijd nodig hebt, maar waarom staat Settings bij je Database en Queue? Settings lijkt me iets dat je apart wilt hebben.

Ten slotte, kleine opmerking, maak die public fields final, anders loop je het risico op race conditions in een multithreaded omgeving.

Ik zou voor een andere opzet gaan, die meer lijkt op het Strategy of Template Method pattern. Kijk eens naar Spring JdbcTemplate voor een voorbeeld. http://kickjava.com/src/o...ore/JdbcTemplate.java.htm
Hiermee wordt je database access veel controleerbaarder en is de kans op lekkende connecties kleiner.

Fat Pizza's pizza, they are big and they are cheezy


Acties:
  • 0 Henk 'm!

Verwijderd

Je zegt dat je vanuit een instantie van objectB, de instantie objectC wilt kunnen aanspreken.

Nou, wat let je om objectC als property toe te voegen (lees: composition) aan objectB, zodat je objectC inderdaad aan kunt spreken vanuit een instantie van objectB?

Acties:
  • 0 Henk 'm!

  • beelie
  • Registratie: Maart 2004
  • Laatst online: 20-09 14:12
eerst en vooral, je gebruikt nooit de classe C ;)
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 
class classA{ 
  public $objectB, $objectC; 
  public function __construct(){ 
    $this->objectB = new classA(); <-
    $this->objectC = new classB(); <-
  } 
} 
class classB{} 
class classC{} 

$objectA = new classA(); 
?>


als C A niet echt nodig heeft, maar wel B:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php 
class classA{ 
  private $objectB, $objectC; 
  public function __construct(){ 
    $this->objectB = new classB(); 
    $this->objectC = new classC($objectB);
  } 
} 
class classB{ 
  public function someMethod(){ 
    //code 
  } 
} 
class classC{
  private $objectB;
  public function __construct( $objectB ){ 
    $this->objectB = $objectB; 
  }
  public function someOtherMethod(){ 
    $objectB->someMethod(); 
  } 
} 
?>

lijkt me logischer dan A doorgeven ...
chain of command best niet te lang maken, geeft anders vieze dingen op den duur

publieke objecten ben ik ook niet echt fan van, daarvoor heb je getters en setters ...

static zou ik alleen gebruiken als je klasses zelf moet aanspreken ipv objecten
die static oplossing ziet er nogal fishy uit: hoe kan de klasse nu weten welk object je aanspreekt
je hebt namelijk objecten (A) die objecten (B,C) hebben en dan vraag je plots aan de klasse (A): voer dit eens uit, terwijl de klasse (A) geen weet heeft van de objecten (A) van zichzelf...
Pagina: 1