Réutilisabilité du codeIT&L@bsCO PMMVersion 1.01, le 29 novembre 2012             Nicolas Le Nardou                       ...
Introduction : contexte projet> Centre de services d’un grand groupe de presse> 20-30 sites en PHP   - eZ Publish, Symfony...
Introduction : contexte projet> A son écriture, le partage du code entre plusieurs sites est :    - Soit déjà acté    - So...
Introduction : problématique> Comment se construire un référentiel de code commun à tous nos  sites ?   - Quel que soit le...
sommaire> 1 – Rappel des principes de conception SOLID> 2 – Etude de cas concrets> 3 – Point sur les tests unitaires     R...
Conception SOLIDRappel     Réutilisabilité du code
conception SOLID> Single Responsibility> Open / Closed> Liskov substitution> Interface segregation> Dependency injection  ...
SOLID vs STUPID> Singleton> Tight coupling> Untestability> Premature Optimization> Indescriptive Naming> Duplication     R...
SOLID : single responsibility> Principe de responsabilité unique :> « Une classe ne fait qu’une et une seule chose »> Envi...
SOLID : open / closed> « Une classe doit être fermée à la modification et ouverte à  l’extension »> Une évolution ne devra...
SOLID : Liskov substitution> « On doit pouvoir substituer à un objet d’une classe X, tout objet  d’une sous classe de X »>...
SOLID : Interface segregation> « Un objet ne devra pas dépendre d’un autre objet mais de son  interface »> Il faut explici...
SOLID : Dependency Injection> « Un objet ne doit pas instancier un autre objet, il doit le recevoir de  l’extérieur »> Inv...
Cas concret #1    Réutilisabilité du code
cas concret #1> Besoin : Injecter dans nos pages des tags javascript (tracking, pub,  …)> Implémentation : Une classe TagS...
cas concret #1> Les mauvaises solutions :   - Faire 2 développements distincts   - Dupliquer la classe et la modifier   - ...
cas concret #1 : implémentation eZ Publishclass TagServer{    private        $rules;    public function __construct()    {...
cas concret #1 : problèmespublic function __construct(){        $this->rules = array();           $ini = eZINI::instance(t...
cas concret #1 : problèmes> Solution : injecter l’objet eZINI dans le constructeur                                        ...
cas concret #1 : eZINI injectéclass TagServer{    private        $rules;    public function __construct(eZINI $ini)    {  ...
cas concret #1 : eZINI injecté> La construction du serveur :$ini = eZINI::instance(tagserver.ini);$server = new TagServer(...
cas concret #1 : eZINI injecté> Couplage désormais faible> Mais problème de sémantique : conceptuellement nous n’avons pas...
cas concret #1 : interface Configurationinterface Configuration{    const SEPARATOR = /;    /**     * Read configuration i...
cas concret #1 : interface Configurationclass TagServer{    private        $rules;    public function __construct(Configur...
cas concret #1 : interface Configuration> La dépendance avec le framework d’eZ Publish est rompue …> … mais notre code ne ...
cas concret #1 : eZConfigurationclass eZConfiguration implements Configuration{    public function read($variableName, $de...
cas concret #1 : eZConfiguration> Appel$configuration = new eZConfiguration();$server = new TagServer($configuration);> Fo...
cas concret #1 : site Symfony> Etape suivante : réutiliser notre classe TagServer sur un site  reposant sur Symfony     Ré...
cas concret #1 : site Symfony> Bien sûr, la classe eZConfiguration ne fonctionnera pas> Il nous faut une classe YamlConfig...
cas concret #1 : site Symfonyclass YamlConfiguration implements Configuration{    public function read($variableName, $def...
cas concret #1 : site Symfony> Construction du serveur :$configuration = new YamlConfiguration();$server = new TagServer($...
cas concret #1 : bilan> Coût du déploiement de notre classe TagServer sur un autre  framework PHP                         ...
Cas concret #2    Réutilisabilité du code
cas concret #2> Nous voulons ajouter des logs à notre classe TagServer> Contraintes :   - Possibilité de les activer / dés...
cas concret #2class TagServer{    private        $logger;    public function __construct(Logger $logger)    {        $this...
cas concret #2 : empilement de paramètresclass TagServer{    private        $rules,        $logger;    public function __c...
cas concret #2 : dépendance faible> Contrairement à la configuration, le logger est une dépendance  faible> Un logger n’es...
cas concret #2 : injection par mutateurclass TagServer{    private        $logger;    public function __construct(Configur...
cas concret #2 : injection par mutateur    private function writeLog($message)    {        if($this->logger !== null)     ...
cas concret #2 : overhead> Les cas présentés sont simples et petits> A dimension d’un projet réel, les overheads de code p...
cas concret #2 : conteneur d’injection> Solution : recours à un conteneur d’injection> Pimple (Sensio Labs)> DI Component ...
cas concret #2 : conteneur communabstract class Container extends Pimple{    public function __construct()    {        $th...
cas concret #2 : conteneur d’injection     Conteneur commun à tous les socles techniques   Conteneurs spécifiques    Réuti...
cas concret #2 : conteneur spécifique (eZ Publish)class eZContainer extends Container{    public function __construct()   ...
cas concret #2 : conteneur d’injection> Et la construction de notre classe : $container = new eZContainer(); $server = $co...
cas concret #2 : conteneur d’injection> Quelques remarques :   - Le conteneur peut s’appuyer sur de la configuration (ex: ...
Et si on testait ?     Réutilisabilité du code
testabilité : souvenez-vousclass TagServer{    private        $rules;    public function __construct()    {        $this->...
testabilité : problématique> Une instance eZ Publish est nécessaire      Problème de performances des tests> Un fichier eZ...
testabilité : ArrayConfiguration> Il faut mocker la configuration      ArrayConfiguration !     Réutilisabilité du code   ...
testabilité : ArrayConfigurationclass ArrayConfiguration implements Configuration{    private $values;    public function ...
testabilité : le test unitaireclass TagServerTest extends PHPUnit_Framework_TestCase{    private        $tagServer;    pub...
testabilité : bilan> C’est testable !> C’est performant !> Le test est facile à maintenir !> Possibilité de tester aussi l...
merci!Réutilisabilité du code     Page 54
si vous avez des questions ?                          Nicolas Le Nardou                          Architecte / Expert Techn...
Prochain SlideShare
Chargement dans…5
×

Réutilisabilité du code PHP

1 145 vues

Publié le

Conférence donnée au PHP Tour Nantes 2012 : Réutilisabilité du code au sein d'un contexte multi-technos basé sur une application concrète des principes de conception SOLID

Publié dans : Technologie
0 commentaire
2 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
1 145
Sur SlideShare
0
Issues des intégrations
0
Intégrations
52
Actions
Partages
0
Téléchargements
0
Commentaires
0
J’aime
2
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Réutilisabilité du code PHP

  1. 1. Réutilisabilité du codeIT&L@bsCO PMMVersion 1.01, le 29 novembre 2012 Nicolas Le Nardou Architecte / Expert Technique PHP nicolas.lenardou@orange.com Réutilisabilité du code Page 1
  2. 2. Introduction : contexte projet> Centre de services d’un grand groupe de presse> 20-30 sites en PHP - eZ Publish, Symfony 2, WordPress, from scratch, …> Forte audience : environ 10M de pages vues / jour> Périmètres fonctionnels très proches> Pression sur les coûts de développement Réutilisabilité du code Page 2
  3. 3. Introduction : contexte projet> A son écriture, le partage du code entre plusieurs sites est : - Soit déjà acté - Soit déjà en cours de discussion> Le partage avec les autres sites du CDS est toujours de l’ordre du possible Réutilisabilité du code Page 3
  4. 4. Introduction : problématique> Comment se construire un référentiel de code commun à tous nos sites ? - Quel que soit le socle technique - Sans renoncer aux apports de ces différents socles (pas de politique du plus petit dénominateur commun)> Objectifs : - Mutualiser la maintenance du code - Réduire les temps de développement Réutilisabilité du code Page 4
  5. 5. sommaire> 1 – Rappel des principes de conception SOLID> 2 – Etude de cas concrets> 3 – Point sur les tests unitaires Réutilisabilité du code Page 5
  6. 6. Conception SOLIDRappel Réutilisabilité du code
  7. 7. conception SOLID> Single Responsibility> Open / Closed> Liskov substitution> Interface segregation> Dependency injection Réutilisabilité du code Page 7
  8. 8. SOLID vs STUPID> Singleton> Tight coupling> Untestability> Premature Optimization> Indescriptive Naming> Duplication Réutilisabilité du code Page 8
  9. 9. SOLID : single responsibility> Principe de responsabilité unique :> « Une classe ne fait qu’une et une seule chose »> Envie de rajouter une fonctionnalité ? Il est temps de créer une nouvelle classe.> Une classe au fonctionnement clairement défini et borné sera plus facilement réutilisable> Une classe aux multiples responsabilités sera fatalement dupliquée pour être adaptée au nouveau besoin Réutilisabilité du code Page 9
  10. 10. SOLID : open / closed> « Une classe doit être fermée à la modification et ouverte à l’extension »> Une évolution ne devrait pas vous faire casser du code, juste en ajouter !> Une classe doit prévoir de pouvoir être étendue sans être réécrite. Réutilisabilité du code Page 10
  11. 11. SOLID : Liskov substitution> « On doit pouvoir substituer à un objet d’une classe X, tout objet d’une sous classe de X »> Corolaire : Une classe utilisant un objet de classe X ne doit pas avoir connaissance des sous classes de X (sous peine de violer le principe open/closed) Réutilisabilité du code Page 11
  12. 12. SOLID : Interface segregation> « Un objet ne devra pas dépendre d’un autre objet mais de son interface »> Il faut expliciter la dépendance réelle au travers d’une interface Réutilisabilité du code Page 12
  13. 13. SOLID : Dependency Injection> « Un objet ne doit pas instancier un autre objet, il doit le recevoir de l’extérieur »> Inversion de contrôle> Pas d’utilisation du mot clé new dans une classe> Injection par constructeur ou mutateur Réutilisabilité du code Page 13
  14. 14. Cas concret #1 Réutilisabilité du code
  15. 15. cas concret #1> Besoin : Injecter dans nos pages des tags javascript (tracking, pub, …)> Implémentation : Une classe TagServer qui calcule la valeur d’un tag en fonction d’un contexte en entrée (url, contenu, …) et d’un jeu de règles - Moteur de règles - Jeu de règles en configuration> Contrainte : A déployer sur : 1. Un site eZ Publish 2. Un site Symfony 2.x Réutilisabilité du code Page 15
  16. 16. cas concret #1> Les mauvaises solutions : - Faire 2 développements distincts - Dupliquer la classe et la modifier - Nombreux paramètres dans le constructeur - Ou toute autre abomination … Réutilisabilité du code Page 16
  17. 17. cas concret #1 : implémentation eZ Publishclass TagServer{ private $rules; public function __construct() { $this->rules = array(); $ini = eZINI::instance(tagserver.ini); $ini->assign(Tags, Rules, $this->rules); } // ...} Réutilisabilité du code Page 17
  18. 18. cas concret #1 : problèmespublic function __construct(){ $this->rules = array(); $ini = eZINI::instance(tagserver.ini); $ini->assign(Tags, Rules, $this->rules);}> Couplage fort : TagServer dépend de eZINI La classe n’est réutilisable que sur un autre site eZ Publish Réutilisabilité du code Page 18
  19. 19. cas concret #1 : problèmes> Solution : injecter l’objet eZINI dans le constructeur Injection de dépendances (SOLID)> On pourra ainsi substituer à une occurrence d’eZINI, un objet d’une sous classe d’eZINI Réutilisabilité du code Page 19
  20. 20. cas concret #1 : eZINI injectéclass TagServer{ private $rules; public function __construct(eZINI $ini) { $this->rules = array(); $ini->assign(Tags, Rules, $this->rules); } // ...} Réutilisabilité du code Page 20
  21. 21. cas concret #1 : eZINI injecté> La construction du serveur :$ini = eZINI::instance(tagserver.ini);$server = new TagServer($ini); Réutilisabilité du code Page 21
  22. 22. cas concret #1 : eZINI injecté> Couplage désormais faible> Mais problème de sémantique : conceptuellement nous n’avons pas besoin d’un eZINI, nous avons plutôt besoin de la configuration. Il nous faut une interface « Configuration » Séparation d’interfaces (SOLID) Réutilisabilité du code Page 22
  23. 23. cas concret #1 : interface Configurationinterface Configuration{ const SEPARATOR = /; /** * Read configuration if exists. Returns default value * otherwise. * * @param string $variableName fully qualified variable name * @param mixed $defaultValue */ public function read($variableName, $defaultValue);} Réutilisabilité du code Page 23
  24. 24. cas concret #1 : interface Configurationclass TagServer{ private $rules; public function __construct(Configuration $config) { $this->rules = $configuration->read( tagserver/Tags/Rules, array() ); }} Réutilisabilité du code Page 24
  25. 25. cas concret #1 : interface Configuration> La dépendance avec le framework d’eZ Publish est rompue …> … mais notre code ne fonctionne plus pour eZ Publish> Il nous faut une implémentation de Configuration reposant sur eZINI Substitution de Liskov (SOLID) Réutilisabilité du code Page 25
  26. 26. cas concret #1 : eZConfigurationclass eZConfiguration implements Configuration{ public function read($variableName, $defaultValue) { list($file, $group, $variable) = explode(self::SEPARATOR, $variableName); $ini = eZINI::instance($file . .ini); $ini->assign($group, $variable, $defaultValue); return $defaultValue; }} Réutilisabilité du code Page 26
  27. 27. cas concret #1 : eZConfiguration> Appel$configuration = new eZConfiguration();$server = new TagServer($configuration);> Fonctionne à nouveau pour eZ Publish - Sans modification de la classe TagServer Open / Closed (SOLID) Réutilisabilité du code Page 27
  28. 28. cas concret #1 : site Symfony> Etape suivante : réutiliser notre classe TagServer sur un site reposant sur Symfony Réutilisabilité du code Page 28
  29. 29. cas concret #1 : site Symfony> Bien sûr, la classe eZConfiguration ne fonctionnera pas> Il nous faut une classe YamlConfiguration Réutilisabilité du code Page 29
  30. 30. cas concret #1 : site Symfonyclass YamlConfiguration implements Configuration{ public function read($variableName, $defaultValue) { list($file, $group, $variable) = explode(self::SEPARATOR, $variableName); $loader = Yaml::parse($file); if(array_key_exists($loader[$group][$variable])) { return $loader[$group][$variable]; } return $defaultValue; }} Réutilisabilité du code Page 30
  31. 31. cas concret #1 : site Symfony> Construction du serveur :$configuration = new YamlConfiguration();$server = new TagServer($configuration);> Et …. c’est tout ! Réutilisabilité du code Page 31
  32. 32. cas concret #1 : bilan> Coût du déploiement de notre classe TagServer sur un autre framework PHP ≈ Coût de développement d’une classe d’adaptation pour accéder à la configuration> Aucune modification de notre classe TagServer n’a été nécessaire> Les classes de la couche d’adaptation sont elles-mêmes réutilisables constitution d’une boîte à outils très rapidement Réutilisabilité du code Page 32
  33. 33. Cas concret #2 Réutilisabilité du code
  34. 34. cas concret #2> Nous voulons ajouter des logs à notre classe TagServer> Contraintes : - Possibilité de les activer / désactiver - Possibilité de se reposer sur le système de log du socle technique utilisé Réutilisabilité du code Page 34
  35. 35. cas concret #2class TagServer{ private $logger; public function __construct(Logger $logger) { $this->logger = $logger; }} Réutilisabilité du code Page 35
  36. 36. cas concret #2 : empilement de paramètresclass TagServer{ private $rules, $logger; public function __construct(Configuration $configuration, Logger $logger) { $this->logger = $logger; $this->rules = $configuration->read( tagserver/Tags/Rules, array() ); }} Réutilisabilité du code Page 36
  37. 37. cas concret #2 : dépendance faible> Contrairement à la configuration, le logger est une dépendance faible> Un logger n’est pas requis pour le fonctionnement de notre classe Injection par mutateur Réutilisabilité du code Page 37
  38. 38. cas concret #2 : injection par mutateurclass TagServer{ private $logger; public function __construct(Configuration $configuration) { $this->logger = null; /* ... */ } public function setLogger(Logger $logger) { $this->logger = $logger; return $this; }} Réutilisabilité du code Page 38
  39. 39. cas concret #2 : injection par mutateur private function writeLog($message) { if($this->logger !== null) { $this->logger->write($message); } }> Et l’appel :$server = new TagServer(new eZConfiguration());$server->setLogger(new eZLogger()); Réutilisabilité du code Page 39
  40. 40. cas concret #2 : overhead> Les cas présentés sont simples et petits> A dimension d’un projet réel, les overheads de code pour construire les objets peuvent devenir pénibles à gérer.> Par exemple, il a fort à parier que le logger soit nécessaire sur de nombreuses classes. Réutilisabilité du code Page 40
  41. 41. cas concret #2 : conteneur d’injection> Solution : recours à un conteneur d’injection> Pimple (Sensio Labs)> DI Component de Symfony (Sensio Labs)> Objet en charge de l’instanciation des autres objets Réutilisabilité du code Page 41
  42. 42. cas concret #2 : conteneur communabstract class Container extends Pimple{ public function __construct() { $this[tagServer] = function ($container){ $server = new TagServer($container[configuration]); $server->setLogger($container[logger]); return $server; }; }} Réutilisabilité du code Page 42
  43. 43. cas concret #2 : conteneur d’injection Conteneur commun à tous les socles techniques Conteneurs spécifiques Réutilisabilité du code Page 43
  44. 44. cas concret #2 : conteneur spécifique (eZ Publish)class eZContainer extends Container{ public function __construct() { parent::__construct(); $this[configuration] = function ($container){ return new eZConfiguration(); }; $this[logger] = $this->share(function ($container){ return new eZLogger(); }); }} Réutilisabilité du code Page 44
  45. 45. cas concret #2 : conteneur d’injection> Et la construction de notre classe : $container = new eZContainer(); $server = $container[tagServer]; Réutilisabilité du code Page 45
  46. 46. cas concret #2 : conteneur d’injection> Quelques remarques : - Le conteneur peut s’appuyer sur de la configuration (ex: Symfony) - Risque de dépendance au conteneur + global state - Dépendances masquées : quid des outils d’analyse ? Réutilisabilité du code Page 46
  47. 47. Et si on testait ? Réutilisabilité du code
  48. 48. testabilité : souvenez-vousclass TagServer{ private $rules; public function __construct() { $this->rules = array(); $ini = eZINI::instance(tagserver.ini); $ini->assign(Tags, Rules, $this->rules); } // ...} Réutilisabilité du code Page 48
  49. 49. testabilité : problématique> Une instance eZ Publish est nécessaire Problème de performances des tests> Un fichier eZINI est également nécessaire Eparpillement du code de test Maintenabilité affaiblie> Et si eZINI était un service à bouchonner ? (comme la db, un webservice ou le filesystem) Réutilisabilité du code Page 49
  50. 50. testabilité : ArrayConfiguration> Il faut mocker la configuration ArrayConfiguration ! Réutilisabilité du code Page 50
  51. 51. testabilité : ArrayConfigurationclass ArrayConfiguration implements Configuration{ private $values; public function __construct(array $values) { $this->values = $values; } public function read($variableName, $defaultValue) { if(array_key_exists($variableName, $this->values)) { return $this->values[$variableName]; } return $defaultValue; }} Réutilisabilité du code Page 51
  52. 52. testabilité : le test unitaireclass TagServerTest extends PHPUnit_Framework_TestCase{ private $tagServer; public function setUp() { $configuration = new ArrayConfiguration(array( tagserver/Tags/Rules => array(/* ... */) )); $this->tagServer = new TagServer($configuration); }} Réutilisabilité du code Page 52
  53. 53. testabilité : bilan> C’est testable !> C’est performant !> Le test est facile à maintenir !> Possibilité de tester aussi les cas à la marge : - Configuration manquante - Configuration erronée - Configuration non consistante - … Réutilisabilité du code Page 53
  54. 54. merci!Réutilisabilité du code Page 54
  55. 55. si vous avez des questions ? Nicolas Le Nardou Architecte / Expert Technique PHP nicolas.lenardou@orange.comRéutilisabilité du code Page 55

×