[PHP] Verschillende image drivers gebruiken

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • ZeroXT
  • Registratie: December 2007
  • Laatst online: 07-10 23:51
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
<?php
interface InterfaceImage {

    public function crop();
    public function resize();
}


class GD implements InterfaceImage {

    public function crop() {}
    public function resize() {}
}


class ImageMagick implements InterfaceImage {
    
    public function crop() {}
    public function resize() {}
    public function rotate() {}
}


class Image {
    
    private $driver;
    private $image;

    public function setDriver($driver) {
        $this -> driver = new $driver();
    }

    public function setImage($image) {
        $this -> image = $image;
    }

    public function __call($method, $params) {

        if(true === is_callable([$this -> driver, $method])) {
            return call_user_func_array([$this -> driver, $method], array_merge([$this -> image], func_get_args()));
        }
    }
}


$image = new Image();
$image -> setDriver('GD');
$image -> setImage('path/to/image');
$image -> crop();
$image -> rotate(); //Dit is enkel beschikbaar bij de ImageMagick driver
?>


Hierboven is een voorbeeldcode van een aantal problemen waar ik tegenaan loop.

Bovenstaande is voorbeeld code welke de gebruiker in staat stelt om afbeeldingen te croppen en te resizen. Het croppen en resizen kan via de GD library van PHP of via ImageMagick, maar ik wil dat de gebruiker maar één class hoeft aan te spreken om de functionaliteit te gebruiken.

Dit is een use case maar dit kan net zo goed met databases classes gemaakt worden o.i.d.

Nu heb ik drie vragen:

Vraag 1:
Ik heb een interface aangemaakt voor beide "drivers" alleen de ImageMagick driver kan veel meer dan de GD driver. Als voorbeeld heb ik een "rotate" functionaliteit toegevoegd welke de GD driver niet ondersteund.
Dat houdt in dat aan de hand van de gekozen driver, functionaliteit wordt toegevoegd aan de Image class. Is dit de juiste gedachtegang of gaat dit tegen bepaalde design patterns regels in?

Vraag 2:
Op dit moment controlleer ik of de functionaliteit bestaat van de driver in de Image class door een universele "__call" method te maken welke dat controlleert. Ik kan ook alle losse functies welke de drivers hebben implementeren maar omdat de GD driver minder functies heeft dan de ImageMagick driver, is dat wellicht geen goed idee omdat er dan een functie bestaat in de Image class voor een functie in de driver die niet hoeft te bestaan. Wat is hier een goed idee in?

Vraag 3:
Is hier een specifieke design pattern voor? Het lijkt namelijk zowel op de factory design pattern als mede op de proxy design pattern.

Ik weet niet goed of ik dit op de "juiste" manier aanpak.

Acties:
  • 0 Henk 'm!

  • Groentjuh
  • Registratie: September 2011
  • Laatst online: 16:31
Mag ik vragen of je van Intervention hebt gehoord? Misschien doet die library al ongeveer wat je wilt.

Acties:
  • 0 Henk 'm!

  • ZeroXT
  • Registratie: December 2007
  • Laatst online: 07-10 23:51
Daar had ik nog niet eerder van gehoord. Echter gaat het hier meer om de use case ipv de functionaliteit van een image class maken.

Acties:
  • 0 Henk 'm!

  • Groentjuh
  • Registratie: September 2011
  • Laatst online: 16:31
Ik weet niet precies welk probleem je wilt oplossen. Heb je in sommige hosts enkel GD en bij andere enkel ImageMagick?

In dat geval wil volgens mij een Bridge pattern maken. De bedoeling van het bridge patroon is om de abstractie en de implementatie los te koppelen van elkaar zodanig dat de twee onafhankelijk van elkaar kunnen variëren.

In jouw verhaal is implementatie #1 met behulp van GD en implementatie #2 met behulp van ImageMagick. Dat is ook precies wat Intervention doet.

In een bridge pattern praat je niet rechtstreeks met de implementatie, dus heb je bij alle implementaties alle functies nodig. Eventueel als een driver het niet kan zou je een exception kunnen gooien, maar ook dat is eigenlijk niet echt wenselijk.

[ Voor 20% gewijzigd door Groentjuh op 08-02-2018 17:00 ]


Acties:
  • 0 Henk 'm!

  • azerty
  • Registratie: Maart 2009
  • Laatst online: 14:13
Als je de functionaliteit op een interface wilt aanspreken moet hij minimaal gedefinieerd zijn.

Als 1 implementatie daarvan er dan niet mee overweg kan en een fout gooit lijkt mij de meer gewenste oplossing. Moet je gewoon zorgen dat je die fouten netjes afhandelt :)

Acties:
  • 0 Henk 'm!

  • Stemis
  • Registratie: Juli 2013
  • Nu online
ZeroXT schreef op donderdag 8 februari 2018 @ 16:32:


Bovenstaande is voorbeeld code welke de gebruiker in staat stelt om afbeeldingen te croppen en te resizen. Het croppen en resizen kan via de GD library van PHP of via ImageMagick, maar ik wil dat de gebruiker maar één class hoeft aan te spreken om de functionaliteit te gebruiken.

Dit is een use case maar dit kan net zo goed met databases classes gemaakt worden o.i.d.

Nu heb ik drie vragen:

Vraag 1:
Ik heb een interface aangemaakt voor beide "drivers" alleen de ImageMagick driver kan veel meer dan de GD driver. Als voorbeeld heb ik een "rotate" functionaliteit toegevoegd welke de GD driver niet ondersteund.
Dat houdt in dat aan de hand van de gekozen driver, functionaliteit wordt toegevoegd aan de Image class. Is dit de juiste gedachtegang of gaat dit tegen bepaalde design patterns regels in?

Vraag 2:
Op dit moment controlleer ik of de functionaliteit bestaat van de driver in de Image class door een universele "__call" method te maken welke dat controlleert. Ik kan ook alle losse functies welke de drivers hebben implementeren maar omdat de GD driver minder functies heeft dan de ImageMagick driver, is dat wellicht geen goed idee omdat er dan een functie bestaat in de Image class voor een functie in de driver die niet hoeft te bestaan. Wat is hier een goed idee in?

Vraag 3:
Is hier een specifieke design pattern voor? Het lijkt namelijk zowel op de factory design pattern als mede op de proxy design pattern.
jou __call implementatie noemen we bij ons een code smell en zou niet door de review komen! :P
Als tip: probeer het gebruik van PHP Magic Methods zoveel mogelijk te beperken, of helemaal niet te gebruiken muv de constructor natuurlijk!

Het is erg netjes dat je interfaces gebruikt.

In dit geval zou ik echter een abstract class maken die in alle body's excepties gooit.
Voorbeeld NotImplementedException.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
class AbstractDriver {
    public function crop() {
        throw new NotImplementedException();
    }

    public function resize() {
        throw new NotImplementedException();
    }

    public function rotate() {
        throw new NotImplementedException();
    }
}


Dan kan je met de ImageMick class of de GD class deze abstract klasse extenden

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ImageMickDriver extends AbstractDriver {
    public function crop() {
        // Do Stuff
    }

    public function resize() {
        // Do Stuff
    }

    public function rotate() {
        // Do Stuff
    }
}

class GDDriver extends AbstractDriver {
    public function crop() {
        // Do Stuff
    }

    public function resize() {
        // Do Stuff
    }
}


Het is ook met interfaces op te lossen door meerdere interfaces te gebruiken(ie RotateInterface)

Maar je moest sowieso ergens af gaan vangen wat er moet gebeuren als rotate() niet bestaat, het netst is om dit via een ExceptionHandler te doen en dus NotImplementedException gebruiken.

Wat nog netter zou zijn is een Feature Toggle, oftewel de rotate functionaliteit uitzetten indien de driver niet aanwezig is op de machine


Vraag 3:
Wat je met de driver probeert te doen is de Strategy Pattern.
Oftewel tijdens runtime bepalen welke driver je wilt gaan gebruiken.

[ Voor 20% gewijzigd door Stemis op 09-02-2018 00:58 ]


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
ZeroXT schreef op donderdag 8 februari 2018 @ 16:32:
Ik weet niet goed of ik dit op de "juiste" manier aanpak.
Voor vraag 1, 2 & 3 kijk gerust hoe ik dat heb opgelost https://bitbucket.org/djm...eviewer=file-view-default

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • pottink
  • Registratie: Augustus 2010
  • Laatst online: 06-10 10:24
Misschien ook interessant:

https://packagist.org/packages/imagine/imagine

Hoef je niet alles opnieuw te gaan schrijven en te doen. Deze package kan alles al doen wat je wil voor je en is goed gedocumenteerd.
Pagina: 1