Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

Open close principle, on a dit étendre, pas extends !

Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Prochain SlideShare
SQL et MySQL
SQL et MySQL
Chargement dans…3
×

Consultez-les par la suite

1 sur 41 Publicité

Open close principle, on a dit étendre, pas extends !

Télécharger pour lire hors ligne

Conférence en français sur le principe d'ouverture fermeture proposé à l'AFUP de Paris en février 2019, comportant des exemples d'implémentation du principe à l'aide de design patterns, avec ou sans utilisation d'un framework.

Conférence en français sur le principe d'ouverture fermeture proposé à l'AFUP de Paris en février 2019, comportant des exemples d'implémentation du principe à l'aide de design patterns, avec ou sans utilisation d'un framework.

Publicité
Publicité

Plus De Contenu Connexe

Diaporamas pour vous (17)

Similaire à Open close principle, on a dit étendre, pas extends ! (20)

Publicité
Publicité

Open close principle, on a dit étendre, pas extends !

  1. 1. Open Close Principle, on a dit étendre, pas extends ! Meetup AFUP, 12/02/2019, @tdutrion
  2. 2. Qui suis-je ? Directeur technique chez Darkmira (on recrute) Développeur back/consultant freelance (Engineor) Organisateur (ScotlandPHP, DarkmiraTour Brasil, AFUP Lorraine, Association Zend Francophone, DijonTech) Grand fan de PHP et son écosystème Avant tout ingénieur logiciel adèpte de systèmes legacy ! (fan de defensive programming)
  3. 3. Open Closed principle Principe ouvert / fermé
  4. 4. Principes SOLID Responsabilité unique (single responsibility) Ouvert / fermé (open / closed) Substitution de Liskov (Liskov substitution) Ségrégation des interfaces (interface segregation) Inversion des dépendances (dependency inversion)
  5. 5. Open Close Principle une classe doit être ouverte à l'extension, mais fermée à la modi cation “ “ « Ouverte » signi e qu'elle a la capacité d'être étendue. « Fermée » signi e qu'elle ne peut être modi ée que par extension, sans modi cation de son code source. “ “
  6. 6. Oui mais comment ?
  7. 7. Avertissement : Pour suivre les parties suivantes, le principe de responsabilité unique est considéré comme acquis.
  8. 8. Use cases / exemples logs email négociation de type de réponse ajout de conditions métier
  9. 9. Approche naïve (~1988) Bertrand Meyer : Héritage Une classe non nale est extensible, car on peut en hériter.
  10. 10. class AireCalculateur { public function carre(Carre $carre): float { return pow($carre->tailleCote()); } public function cercle(Cercle $cercle): float { return pow($cercle->radius()) *pi(); } // ... // Autres formes // ... }
  11. 11. Héritage surcharge de méthode abstraction abstract class Forme { public abstract aire(): float; } class AireCalculateur { public function calcul(Forme $forme): float { return $forme->aire(); } }
  12. 12. Abstraction class Carre { private $tailleCote; public function aire(): float { return pow($this->tailleCote); } } class Cercle { private $radius; public function aire(): float { return pow($this->radius) * pi(); } }
  13. 13. Composition Ajout business : nous devons maintenant calculer l'aire de choses dans deux contextes différents. Par exemple : géométrie (ci-dessus) métier (par exemple aire de surface au sol d'un pneu de voiture) L'héritage devient étrange...
  14. 14. Qu'avons nous appris depuis?
  15. 15. Design patterns decorator observer visitor speci cation strategy
  16. 16. Decorator class MonRepository { public function get(Uuid $identifier): MonEntite { // whatever // return [MonEntite] } } $repo = new MonRepository(); $repo->get($monEntite); // pas de logs le client a besoin de logs sur un repository“ “
  17. 17. class MonLoggerRepository extends MonRepository { public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function get(Uuid $identifier): MonEntite { $this->logger->debug('faut mettre un truc là'); $instance = parent::get($identifier); $this->logger->debug('ça devient verbeux'); return $instance; } } $repo = new MonLoggerRepository(); $repo->get($identifier); // log
  18. 18. class MonLoggerRepository extends MonRepository { public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function get(Uuid $identifier): MonEntite { $this->logger->debug('faut mettre un truc là'); $instance = parent::get($identifier); $this->logger->debug('ça devient verbeux'); return $instance; } } $repo = new MonLoggerRepository(); $repo->save($monEntite); // log
  19. 19. interface MonRepository { public function get(Uuid $identifier): MonEntite; } final class MonImplementationRepository implements MonRepository { public function get(Uuid $identifier): MonEntite { return new MonEntite; } }
  20. 20. final class MonLoggerRepository implements MonRepository { public function __construct( LoggerInterface $logger, MonRepository $repository ) { $this->logger = $logger; $this->repository = $repository; } public function get(Uuid $identifier): MonEntite { $this->logger->debug('faut mettre un truc là'); $instance = $this->repository->get($identifier); $this->logger->debug('ça devient verbeux'); return $instance; } }
  21. 21. Ajoutons un cache : final class MonCacheRepository implements MonRepository { public function __construct(CacheInterface $cache, MonReposi $this->cache = $cache; $this->repository = $repository; } public function get(Uuid $identifier): MonEntite { if ($this->cache->has(self::CACHE_KEY)) { return $this->cache->get(self::CACHE_KEY); } $instance = $this->repository->get($identifier); $this->cache->set(self::CACHE_KEY, $instance); return $instance; } }
  22. 22. Utilisation // $cache = // $logger = $repo = new MonCacheRepository( MonLoggerRepository( MonImplementationRepository(), $logger ), $cache ); // $cache = // $logger = $repo = new MonLoggerRepository( $logger, MonCacheRepository( $cache, MonImplementationRepository() ) );
  23. 23. Simpli ons Dépendances : interface CacheInterface { public function get(string $cle): MonEntite; public function has(): bool; public function set(MonEntite $entite, string $cle): void } interface LoggerInterface { public function debug(string $details): void; }
  24. 24. $container = [ MonRepository::class => function($container) { return $container[MonLoggerRepository::class]($container }, MonLoggerRepository::class => function($container) { return new MonLoggerRepository( $container[LoggerInterface::class]($container), $container[MonCacheRepository::class]($container) ); }, MonCacheRepository::class => function($container) { return new MonCacheRepository( $container[CacheInterface::class]($container), $container[MonImplementationRepository::class]($cont ); }, MonImplementationRepository::class => function($container) return new MonImplementationRepository(); }, // to be continued
  25. 25. CacheInterface::class => function($container) { return new class implements CacheInterface { public function get(string $cle): MonEntite { return new MonEntite; } public function has(): bool { return false; } public function set(MonEntite $entite, string $cle) }; }, LoggerInterface::class => function($container) { return new class implements LoggerInterface { public function debug(string $details): void { } }; }, ];
  26. 26. Appel complet $factory = $container[MonRepository::class]; $repo = $factory($container); var_dump($repo); reportez-vous au D de SOLID pour simpli er (ici MonRepository ) “ “
  27. 27. Tout le monde fait du SF maintenant
  28. 28. Demo time! composer create-project symfony/skeleton composer req twig ... AppRepositoryMonRepository: alias: AppRepositoryMonImplementationRepository AppRepositoryMonLoggerRepository: decorates: AppRepositoryMonImplementationRepository
  29. 29. Avec les deux : AppRepositoryMonRepository: alias: AppRepositoryMonImplementationRepository AppRepositoryMonLoggerRepository: decorates: AppRepositoryMonImplementationRepository arguments: [ '@AppLoggerInterface', '@AppRepositoryMonLoggerRepository.inner' ] AppRepositoryMonCacheRepository: decorates: AppRepositoryMonImplementationRepository arguments: [ '@AppCacheInterface', '@AppRepositoryMonCacheRepository.inner' ]
  30. 30. Bonus : decoration_priority: 1
  31. 31. Observer
  32. 32. En Symfony : les evènements (vous connaissez plus hein )
  33. 33. final class MonImplementationRepository { private $dispatcher; public function __construct( EventDispatcherInterface $dispatcher ) { $this->dispatcher = $dispatcher; } public function get(Uuid $identifier): MonEntite { $this->dispatcher->dispatch('mon_super_event.pre', $value = new MonEntite; $this->dispatcher->dispatch('mon_super_event.post', return $value; } }
  34. 34. final class RepositoryGetListener { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function demo(MonSuperEvent $event) { $this->logger->debug("Quelqu'un a récup un truc"); } } AppListenerRepositoryGetListener: tags: - name: kernel.event_listener event: mon_super_event.pre method: demo
  35. 35. Cadeau : event dans un decorator final class MonEventRepository implements MonRepository { private $repository; private $dispatcher; public function __construct(EventDispatcherInterface $dispat { $this->repository = $repository; $this->dispatcher = $dispatcher; } public function get(Uuid $identifier): MonEntite { $this->dispatcher->dispatch('mon_super_event.pre', $instance = $this->repository->get($identifier); $this->dispatcher->dispatch('mon_super_event.post', return $instance;

×