Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Object Reorientation

Object Reorientation

http://verraes.net/2014/11/object-reorientation/

Talk for DrupalCamp Ghent, November 2014

330627d5f564b710721236077903ed60?s=128

Mathias Verraes

November 07, 2014
Tweet

Transcript

  1. Object Reorientation @mathiasverraes

  2. Mathias Verraes Independent Consultant @mathiasverraes verraes.net

  3. Systems are all about behaviour

  4. State as a side effect

  5. Functions f(x, y) -> z

  6. What does it do? f(x, y) -> z

  7. Types f(x, y) -> z where x : "A dog

    named Charles" y : [[1, 4], [76, "D5004375571"]] z : WAT?!
  8. Types f(x, y) -> z where x : int y

    : int z : 1 | 0 | -1
  9. Types function compare($left, $right) { if($left > $right) return 1;

    elseif($left == $right) return 0; else return -1; }
  10. Hacklang function compare(int $left, int $right) : int { if($left

    > $right) return 1; elseif($left == $right) return 0; else return -1; }
  11. Types are logical propositions about your code

  12. Types make it easier for programs to reason about programs

  13. Tools for typing in PHP: Classes interfaces type hints

  14. Types prevent type bugs function send( $articleId, $subscriberId ) send($subscriberId,

    $articleId) // bug function send( ArticleId $articleId, SubscriberId $subscriberId )
  15. Recurring pattern f(d : T, a, b) -> d' :

    T
  16. Recurring pattern function doOperation($onData, $withArg, $andArg) { // ... return

    $newData; } function otherOp($onData, $withArg) { // ... return $newData; }
  17. Encapsulation

  18. $article = [ 'title' => "Object Reorientation", 'body' => "Encapsulate

    all the things", 'author' => "@mathiasverraes", 'published' => "no", ]; $article['published'] = "yes";
  19. without encapsulation, changes propagate $article = [ 'title' => "Object

    Reorientation", 'body' => "Encapsulate all the things", 'author' => "@mathiasverraes", 'published' => false, ]; $publishedArticle = publish($article, 10); $publishedArticle = [ 'title' => "Object Reorientation", 'body' => "Encapsulate all the things", 'author' => "@mathiasverraes", 'published' => false, ];
  20. That's not what arrays are for

  21. Arrays are collections of values of the same type

  22. NOT OK $article = [ 'title' => "Object Reorientation", 'body'

    => "Encapsulate all the things", 'author' => "@mathiasverraes", 'published' => false, ];
  23. OK $fib = [1, 1, 2, 3, 5, 8, 13];

    $articles = [ $objectReorientation, $understandingFibonacci, ]; $indexedArticles = [ 34 => $objectReorientation, 951 => $understandingFibonacci, ];
  24. Encapsulation doOperation($onData, $withArg, $andArg) -> $object->doOperation($withArg, $andArg) $article->publish()

  25. Design objects from the outside $article = new Article($title, $body,

    $author); $article->publish(); $this->setExpectedException( CantChangePublishedArticle::class ); $this->rename($newTitle); // throws!
  26. Objects guard their own consistency $article = new Article(); //

    <-inconsistent $article->setTitle($title); // <-inconsistent $article->setBody($body); // <-inconsistent $article->setAuthor($author); // <-consistent
  27. Objects guard their own consistency final class Article { private

    $title, $body, $author; public function __construct($title, $body, $author) { if(empty($title) || !is_string($title)) { throw new InvalidArgumentException; } // etc... $this->body = $body; $this->author = $author; $this->title = $title; } }
  28. Objects are contracts final class StoryUpdates { public function notify(Article

    $source, Subscriber $subscriber) { // ... } }
  29. Objects compose final class Article { private $title; private $body;

    private $author; public function __construct( Title $title, Body $body, Author $author ) { $this->body = $body; $this->author = $author; $this->title = $title; } }
  30. Objects decompose final class TimeSlot { public function __construct( Time

    $startTime, Time $endTime ) { // ... } }
  31. Object patterns

  32. Value Object final class TwitterHandle { private $name; public function

    __construct($name) { if (!preg_match('/^[A-Za-z0-9_]{1,15}$/', $name)) { throw new InvalidArgumentException; } $this->name = $name; } public function __toString() { return $this->name; } }
  33. No, you will not have too many objects.

  34. Trust me on that.

  35. Value Object final class Money { private $amount; private $currency;

    public function __construct($amount, Currency $currency) { // guards ... $this->amount = $amount; $this->currency = $currency; } }
  36. attracts behaviour final class Money { /** @return Money **/

    public function add(Money $other) /** @return Money[] **/ public function allocate([6, 3, 1]) }
  37. values are immutable $jim_price = new Money(2500, new Currency('EUR')); $hannah_price

    = $jim_price; $coupon = new Money(500, new Currency('EUR')); $jim_price->subtract($coupon); $jim_price->equals($hannah_price); // true!
  38. values objects are immutable $new_price = $jim_price->subtract($coupon);

  39. values objects are immutable /** @return Money **/ public function

    add(Money $other) { if(!$this->hasSameCurrencyAs($other)){ throw new CantAddDifferentCurrencies; } return new Money( $this->amount + $other->amount, $this->currency ); }
  40. Entity final class Article { private $id; private $title; private

    $published = false; public function __construct(ArticleId $id, Title $title) { $this->id = $id; $this->title = $title; } public function publish() { $this->published = true; } }
  41. lifecycle changes over time stable identity

  42. Repositories collect entities final class ArticleRepository { private $db; public

    function __construct(Database $db) /** @return Article */ public function find(ArticleId $articleId) /** @return Article[] */ public function findAll() /** @return Article[] */ public function findByAuthor(Author $author) }
  43. Procedural -> abstractions into objects

  44. $countOrders = DB::query( 'SELECT COUNT(*) FROM orders WHERE customerId =

    :customerId', [ 'customerId' => $customer['id']] ); if($countOrders > 5) { $template = 'gold.html'; sendMail($template, ...); } else { $totalAmountSpent = DB::query( 'SELECT ...', [ 'customerId' => $customer['id']] ); if($totalAmountSpent > 5000) { $template = 'silver.html'; sendMail($template, ...); } else { $template = 'bronze.html'; sendMail($template, ...); } }
  45. $customer['id'] vs $customer->getId();

  46. Specification final class CustomerHas5Orders { private $db; public function __construct(DB

    $db) { $this->db = $db; } /** @return bool */ public function isSatisfiedBy(Customer $customer) { // ... } }
  47. Strategy final class MailingTemplateStrategy { //... private $strategy; public function

    __construct(Db $db, Mailer $mailer) { //... $this->strategy = [ 'gold.html' => new CustomerHas5Orders($db), 'silver.html' => new CustomerSpent5000EUR($db), 'bronze.html' => new CustomerBought1Product($db), ]; } public function getTemplateFor(Customer $customer) { // ... return $template; } }
  48. Polymorphism final class TenantAStrategy implements MailingTemplateStrategy { /* ... */}

    final class TenantBStrategy implements MailingTemplateStrategy { /* ... */}
  49. Give up control $campaign = new PromotionCampagin( new TenantAStrategy() );

    tell a story $campaign->sendNewsletterTo( $customers->registeredBefore($date) );
  50. looks like code $countOrders = DB::query( 'SELECT COUNT(*) FROM orders

    WHERE customerId = :customerId', [ 'customerId' => $customer['id']] ); if($countOrders > 5) { $template = 'gold.html'; sendMail($template, ...); } else { $totalAmountSpent = DB::query( 'SELECT ...', [ 'customerId' => $customer['id']] ); if($totalAmountSpent > 5000) { $template = 'silver.html'; sendMail($template, ...); } else { $template = 'bronze.html'; sendMail($template, ...); } }
  51. looks like business $campaign->sendNewsletterTo( $customers->registeredBefore($date) );

  52. great programming is great Storytelling

  53. verraes.net @mathiasverraes

  54. Links » Final Classes, Mathias Verraes » DRY is about

    Knowledge, Mathias Verraes » Value Objects, Tony Piper » Object Thinking, David West » Patterns of Enterprise Application Architecture, Martin Fowler » Domain-Driven Design, Eric Evans
  55. inheritance is (mostly) evil