How I started to love design patterns

47
How I s tar ted to love what they call Design Pat terns @samuelr oze

Transcript of How I started to love design patterns

Page 1: How I started to love design patterns

How I started to love what they call

Design Patterns@samuelroze

Page 2: How I started to love design patterns

Samuel Roze

Software Enginner @ InviqaFounder @ ContinuousPipe.io

4 twitter.com/samuelroze

4 github.com/sroze

4 sroze.io

Page 3: How I started to love design patterns

I started years ago...

Page 4: How I started to love design patterns

I probably wrote that<?php

include 'header.php';include 'pages/'. $_GET['page'].'.php';include 'footer.php';

Page 5: How I started to love design patterns

At some point I used Symfonyclass EntityController extends Controller{ public function myAction($identifier) { $entity = $this->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository(Entity::class) ->find($identifier);

return $this->render('my-template.html.twig', [ 'entity' => $entity, ]); }}

Page 6: How I started to love design patterns

Then I updated my entity...public function myAction(Request $request, $identifier){ // ... if ($request->isMethod('POST')) { $form->handleRequest($request);

if ($form->isValid()) { $entity = $form->getData();

$doctrine->persist($entity); $doctrine->flush($entity); } } // ...}

Page 7: How I started to love design patterns

Then I even sent an email// ...

if ($form->isValid()) { $entity = $form->getData();

$doctrine->persist($entity); $doctrine->flush($entity);

$mailer = $this->getContainer()->get('mailer'); $mailer->send( // ... );}

// ...

Page 8: How I started to love design patterns

And then...It became unmaintanable

Page 9: How I started to love design patterns

Design Patterns

Page 10: How I started to love design patterns

A general reusable solution to a commonly occurring problem within a given

context.1

Wikipedia

Page 11: How I started to love design patterns
Page 12: How I started to love design patterns

All the design patterns do the same thing: control the

information flow.1

Anthony Ferrara

Page 13: How I started to love design patterns

Let's do some refactoringclass EntityController extends Controller{ public function myAction(Request $request, $identifier) { $entity = $this->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository(Entity::class) ->find($identifier);

$form = $this->createForm(EntityFormType::class, $entity); $form->handleRequest($request);

if ($form->isValid()) { $entity = $form->getData();

$doctrine->persist($entity); $doctrine->flush($entity);

$mailer = $this->getContainer()->get('mailer'); $mailer->send( \Swift_Message::newInstance() ->setBody('Hey, something was updated!') ); }

return $this->render('my-template.html.twig', [ 'entity' => $entity, ]); }}

Page 14: How I started to love design patterns

Why do we refactor?4 We want to reuse code

Page 15: How I started to love design patterns

Why do we refactor?4 We want to reuse code

4 So we delegate the responsibilities

Page 16: How I started to love design patterns

Why do we refactor?4 We want to reuse code

4 So we delegate the responsibilities

4 And improve the readability

Page 17: How I started to love design patterns

Why do we refactor?4 We want to reuse code

4 So we delegate the responsibilities

4 And improve the readability

4 And therefore we ensure maintainability

Page 18: How I started to love design patterns

Why do we refactor?4 We want to reuse code

4 So we delegate the responsibilities

4 And improve the readability

4 And therefore we ensure maintainability

4 By doing so we enable change

Page 19: How I started to love design patterns

Adapter

Page 20: How I started to love design patterns
Page 21: How I started to love design patterns

Adapterinterface EntityRepository{ public function find($identifier) : Entity;

public function save(Entity $entity) : Entity;}

Page 22: How I started to love design patterns

Adapterfinal class DoctrineEntityRepository implements EntityRepository{ private $entityManager;

public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; }

public function find($identifier) : Entity { if (null === ($entity = $this->getDoctrineRepository()->find($identifier))) { throw new EntityNotFound(); }

return $entity; }

// ...}

Page 23: How I started to love design patterns

Adapterfinal class DoctrineEntityRepository implements EntityRepository{ // ...

public function save(Entity $entity) : Entity { $entity = $this->entityManager->merge($entity);

$this->entityManager->persist($entity); $this->entityManager->flush($entity);

return $entity; }}

Page 24: How I started to love design patterns

class EntityController extends Controller{ public function myAction(Request $request, $identifier) { $entity = $this->getEntityRepository()->find($identifier);

// ...

if ($form->isValid()) { $this->getEntityRepository()->save($entity);

$mailer = $this->getContainer()->get('mailer'); $mailer->send( \Swift_Message::newInstance() ->setBody('Hey, something was updated!') ); } }}

Page 25: How I started to love design patterns

/** @Route(service="controller.entity") **/class EntityController{ public function __construct(EntityRepository $entityRepository, /** ... **/) { /** ... **/ }

public function myAction(Request $request, $identifier) { $entity = $this->entityRepository->find($identifier);

// ...

if ($form->isValid()) { $this->entityRepository->save($entity);

$this->mailer->send( \Swift_Message::newInstance() ->setBody('Hey, something was updated!') ); } }}

Page 26: How I started to love design patterns

The XML bit<service id="entity.repository.doctrine" class="App\Infrastructure\DoctrineEntityRepository"> <argument type="service" id="doctrine.orm.entity_manager" /></service>

Page 27: How I started to love design patterns

The XML bit<service id="entity.repository.doctrine" class="App\Infrastructure\DoctrineEntityRepository"> <argument type="service" id="doctrine.orm.entity_manager" /></service>

<service id="entity.repository" alias="entity.repository.doctrine" />

Page 28: How I started to love design patterns

The XML bit<service id="entity.repository.doctrine" class="App\Infrastructure\DoctrineEntityRepository"> <argument type="service" id="doctrine.orm.entity_manager" /></service><service id="entity.repository" alias="entity.repository.doctrine" />

<service id="controller.entity" class="AppBundle\Controller\EntityController">

<argument type="service" id="entity.repository" /> <argument type="service" id="form.factory" /> <argument type="service" id="mailer" /></service>

Page 29: How I started to love design patterns

Store the entity in-memory?final class InMemoryRepositoryEntity implements EntityRepository{ private $entities = [];

public function find($identifier) : Entity { if (!array_key_exists($identifier, $this->entities)) { throw new EntityNotFound(); }

return $this->entities[$identifier]; }

public function save(Entity $entity) : Entity { $this->entities[$entity->getIdentifier()] = $entity;

return $entity; }}

Page 30: How I started to love design patterns

Event Dispatcher

Page 31: How I started to love design patterns
Page 32: How I started to love design patterns

Dispatching the eventclass MyController{ public function myAction(Request $request, $identifier) { // ... if ($form->isValid()) { $this->entityRepository->save($entity); $this->eventDispatcher->dispatch( EntitySaved::NAME, new EntitySaved($entity) ); } // ... }}

Page 33: How I started to love design patterns

An eventclass EntitySaved extends Event{ const NAME = 'entity.saved';

private $entity;

public function __construct(Entity $entity) { $this->entity = $entity; }

public function getEntity() : Entity { return $this->entity; }}

Page 34: How I started to love design patterns

A listenerfinal class SendAnEmailWhenEntityIsSaved{ public function __construct(MailerInterface $mailer) { /** ... **/ }

public function onEntitySaved(EntitySaved $event) { $this->mailer->send(/** something **/); }}

Page 35: How I started to love design patterns

Because we want some XML<service id="controller.entity" class="AppBundle\Controller\EntityController">

<argument type="service" id="entity.repository" /> <argument type="service" id="form.factory" /> <argument type="service" id="event_dispatcher" /></service>

-

Page 36: How I started to love design patterns

Because we want some XML<service id="controller.entity" class="AppBundle\Controller\EntityController">

<argument type="service" id="entity.repository" /> <argument type="service" id="form.factory" /> <argument type="service" id="event_dispatcher" /></service>

<service id="entity.listener.send_mail_when_saved" class="App\Entity\Listener\SendAnEmailWhenEntityIsSaved"> <argument type="service" id="mailer" />

<tag name="kernel.event_listener" event="entity.saved" /></service>

Page 37: How I started to love design patterns

What is the point?4 We can create another listener easily

4 We just have to dispatch this event

Page 38: How I started to love design patterns

Decorator

Page 39: How I started to love design patterns

final class DispatchAnEventWhenEntityIsSaved implements EntityRepository{ public function __construct( EntityRepository $decoratedRepository, EventDispatcherInterface $eventDispatcher ) { /** ... **/ }

public function find($identifier) : Entity { return $this->decoratedRepository($identifier); }

public function save(Entity $entity) : Entity { $saved = $this->decoratedRepository->save($entity);

$this->eventDispatcher->dispatch( EntitySaved::NAME, new EntitySaved($saved) );

return $saved; }}

Page 40: How I started to love design patterns

Decorates the repository<service id="entity.repository.dispatch_an_event_when_saved" class="App\Entity\Repository\DispatchAnEventWhenEntityIsSaved" decorates="entity.repository">

<argument type="service" id="entity.repository.dispatch_an_event_when_saved.inner" />

<argument type="service" id="event_dispatcher" /></service>

Page 41: How I started to love design patterns

We just have to saveclass MyController{ public function myAction(Request $request, $identifier) { // ... if ($form->isValid()) { $this->entityRepository->save($entity); } // ... }}

Page 42: How I started to love design patterns

Decorates all the things!

Page 43: How I started to love design patterns

What do we have?4 Flexible entity storage

4 Events dispatched regarding the storage

4 Optional actions on events

4 Easily testable code!

Page 44: How I started to love design patterns

Good practices, design patterns, they just help us

to enable change

Page 45: How I started to love design patterns

How to apply that...4 Closes the door!

4 Uses final keyword

4 private properties

4 Create extension points when required

4 Prevent in case of

Page 46: How I started to love design patterns

Maintainability4 Distinct features in their own namespaces

4 Interfaces's names should reflect their responsibilities

4 Implementations' names should reflect their distinction

Page 47: How I started to love design patterns

Thank you!@samuelroze

continuouspipe.io

https://joind.in/talk/187a4