Problemen met het ontwerp van een aantal klassen

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
Ik worstel een beetje met het ontwerp van een paar klassen die ik aan het programmeren ben.

Om (HTML-)tabellen te genereren, heb ik een GridBuilder bedacht. Deze GridBuilder itereert over een verzameling gegevens en maakt voor iedere iteratie een Row aan. Voor iedere Row wordt er over een verzameling RowTransformer's geïtereerd. Zo'n RowTransformer voegt bijvoorbeeld één of meerdere kolommen aan de Row toe of verbergt of highlight de Row. Het eindresultaat is een Grid met Row's die Column's bevatten.

Dit is de (ingekorte) code van de GridBuilder:

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
<?php

class GridBuilder
{
    private $rowTransformers;
    
    public function addRowTransformer(RowTransformer $rowTransformer)
    {
        $this->rowTransformers[] = $rowTransformer;
    }
    
    public function build(array $dataItems)
    {        
        $grid = new Grid();
        
        foreach($dataItems as $dataItem) {
            $row = new Row();
                
            foreach($this->rowTransformers as $rowTransformer) {
                $rowTransformer->transformRow($dataItem, $row);
            }
                
            $grid->addRow($row);
        }
        
        return $grid;
    }
}


Iedere RowTransformer is opgebouwd volgens een contract:

PHP:
1
2
3
4
5
6
<?php

interface RowTransformer
{
    public function transformRow($dataItem, Row $row);
}


Zo ziet een concrete RowTransformer eruit:

PHP:
1
2
3
4
5
6
7
8
9
<?php

class TextRowTransformer implements RowTransformer
{
    public function transformRow($dataItem, Row $row)
    {
        $row->addColumn(new TextColumn($dataItem));
    }
}


So far geen problemen. Echter, nu heb ik deze specifieke UsersGridBuilder gemaakt:

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
<?php

class UsersGridBuilder extends GridBuilder
{
    private $rowTransformers;
    
    public function addRowTransformer(UsersRowTransformer $rowTransformer)
    {
        $this->rowTransformers[] = $rowTransformer;
    }
    
    public function build(array $users, array $rowTransformers)
    {        
        $grid = new Grid();
        
        foreach($users as $user) {
            $row = new Row();
                
            foreach($this->rowTransformers as $rowTransformer) {
                $rowTransformer->transformRow($user, $row);
            }
                
            $grid->addRow($row);
        }
        
        return $grid;
    }
}


Er is nu ook een UsersRowTransformer-interface om af te dwingen dat er een User-object wordt meegestuurd:

PHP:
1
2
3
4
5
6
<?php

interface UsersRowTransformer extends RowTransformer
{
    public function transformRow(User $user, Row $row);
}


Dit is dus waar m'n problemen beginnen. Ik kan nu namelijk niet meer m'n generieke RowTransformer's gebruiken, zoals bijvoorbeeld een spacer.

Nu had ik zelf een twee mogelijke oplossingen bedacht:
  1. De specifieke UsersRowTransformer's schrappen en iedere RowTransformer een 'universele' $payload meegeven. De RowTransformer moet dan zelf maar uitvogelen of het de correcte data heeft meegekregen.
  2. Een Spacer maken die de interface UsersRowTransformer implementeert. Lijkt me vrij lelijk.
Geen van beide oplossingen lijken me echt ideaal. Heeft iemand andere suggesties voor een beter ontwerp?

Alle reacties


Acties:
  • +1 Henk 'm!

  • bomberboy
  • Registratie: Mei 2007
  • Laatst online: 02:47

bomberboy

BOEM!

Ik ben geen geen PHP-expert maar enkele algemene opmerkingen & suggesties:

Om specifieke "configuratie" data mee te geven (zoals de user), of ook alle data is het gebruikelijk dat via de constructor te doen. Je kan op die manier zelfs ook de data zelf uit de methode-signatuur kunnen halen. Maar, een en ander hangt af van hoe je de architectuur wil organiseren. Maar kort door de bocht: alles wat afwijkt van de standaard argumenten -> verhuizen naar de constructor.

Als alternatief wordt er soms voor een soort van initialisatiemethode geopteerd. (Een constructor is eigenlijk ook net dat). Het in een aparte methode stoppen kan relevant zijn indien bepaalde resources niet beschikbaar zijn of "onbetrouwbaar". Bijvoorbeeld indien je een netwerksocket moet opzetten voor communicatie is het niet altijd relevant dit al in de constructor te doen.

Zoals je zelf aanhaalt: een algemeen argument-object of variabele argumentenlijst kan in theorie maar is (naar mijn persoonlijke mening) zelden een goede oplossing die vaak extra foutgevoelig is.


Algemeen over je architectuur: ik verwacht dat je met de huidige opzet in problemen gaat komen indien bepaalde kolommen minder data bevatten dan andere. Dan krijg je potentieel rare output. (tabel waar rij één 4 kolommen heeft en rij twee 3 bijvorbeeld).
Een mogelijke architectuur die daar kan bij helpen is om een klasse te bouwen die de data opvraagt en output genereert maar per cel in je tabel. Noemen we deze dan bijvoorbeeld DataViz,waarbij je dan een methode getData(i,j) hebt met i,j de x,y coördinaten van die cel.

De data op een bepaalde wijze formateren/visualiseren (wat ik denk dat je wil bereiken) kan dan door de output van de getData(i,j) methode door een transformer te sturen die exact weet wat er moet verwerkt worden.

Alternatief kan je die ook toevoegen aan de DataViz klasse zodat die deze rechtstreeks kan aanroepen en altijd bv een stukje geldige html genereert als output van de getData methode. (inversion of control design pattern)

Een structuur als deze geeft je heel flexibiliteit om de output aan te passen naar je wensen/noden.

Hopelijk is het een beetje duidelijk verwoord :-)