• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Réutilisabilité du code PHP
 

Réutilisabilité du code PHP

on

  • 791 views

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

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

Statistics

Views

Total Views
791
Views on SlideShare
758
Embed Views
33

Actions

Likes
1
Downloads
0
Comments
0

1 Embed 33

http://www.scoop.it 33

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Réutilisabilité du code PHP Réutilisabilité du code PHP Presentation Transcript

    • 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
    • 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
    • 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
    • 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
    • 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
    • Conception SOLIDRappel Réutilisabilité du code
    • conception SOLID> Single Responsibility> Open / Closed> Liskov substitution> Interface segregation> Dependency injection Réutilisabilité du code Page 7
    • SOLID vs STUPID> Singleton> Tight coupling> Untestability> Premature Optimization> Indescriptive Naming> Duplication Réutilisabilité du code Page 8
    • 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
    • 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
    • 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
    • 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
    • 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
    • Cas concret #1 Réutilisabilité du code
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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
    • cas concret #1 : eZINI injecté> La construction du serveur :$ini = eZINI::instance(tagserver.ini);$server = new TagServer($ini); Réutilisabilité du code Page 21
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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
    • cas concret #1 : site Symfony> Etape suivante : réutiliser notre classe TagServer sur un site reposant sur Symfony Réutilisabilité du code Page 28
    • 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
    • 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
    • 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
    • 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
    • Cas concret #2 Réutilisabilité du code
    • 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
    • cas concret #2class TagServer{ private $logger; public function __construct(Logger $logger) { $this->logger = $logger; }} Réutilisabilité du code Page 35
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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
    • cas concret #2 : conteneur d’injection Conteneur commun à tous les socles techniques Conteneurs spécifiques Réutilisabilité du code Page 43
    • 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
    • cas concret #2 : conteneur d’injection> Et la construction de notre classe : $container = new eZContainer(); $server = $container[tagServer]; Réutilisabilité du code Page 45
    • 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
    • Et si on testait ? Réutilisabilité du code
    • 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
    • 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
    • testabilité : ArrayConfiguration> Il faut mocker la configuration ArrayConfiguration ! Réutilisabilité du code Page 50
    • 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
    • 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
    • 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
    • merci!Réutilisabilité du code Page 54
    • si vous avez des questions ? Nicolas Le Nardou Architecte / Expert Technique PHP nicolas.lenardou@orange.comRéutilisabilité du code Page 55