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

Mathias Verraes

November 07, 2014
Tweet

More Decks by Mathias Verraes

Other Decks in Programming

Transcript

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

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

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

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

    > $right) return 1; elseif($left == $right) return 0; else return -1; }
  5. Types prevent type bugs function send( $articleId, $subscriberId ) send($subscriberId,

    $articleId) // bug function send( ArticleId $articleId, SubscriberId $subscriberId )
  6. Recurring pattern function doOperation($onData, $withArg, $andArg) { // ... return

    $newData; } function otherOp($onData, $withArg) { // ... return $newData; }
  7. $article = [ 'title' => "Object Reorientation", 'body' => "Encapsulate

    all the things", 'author' => "@mathiasverraes", 'published' => "no", ]; $article['published'] = "yes";
  8. 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, ];
  9. NOT OK $article = [ 'title' => "Object Reorientation", 'body'

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

    $articles = [ $objectReorientation, $understandingFibonacci, ]; $indexedArticles = [ 34 => $objectReorientation, 951 => $understandingFibonacci, ];
  11. Design objects from the outside $article = new Article($title, $body,

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

    <-inconsistent $article->setTitle($title); // <-inconsistent $article->setBody($body); // <-inconsistent $article->setAuthor($author); // <-consistent
  13. 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; } }
  14. 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; } }
  15. 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; } }
  16. Value Object final class Money { private $amount; private $currency;

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

    public function add(Money $other) /** @return Money[] **/ public function allocate([6, 3, 1]) }
  18. 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!
  19. 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 ); }
  20. 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; } }
  21. 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) }
  22. $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, ...); } }
  23. Specification final class CustomerHas5Orders { private $db; public function __construct(DB

    $db) { $this->db = $db; } /** @return bool */ public function isSatisfiedBy(Customer $customer) { // ... } }
  24. 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; } }
  25. Polymorphism final class TenantAStrategy implements MailingTemplateStrategy { /* ... */}

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

    tell a story $campaign->sendNewsletterTo( $customers->registeredBefore($date) );
  27. 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, ...); } }
  28. 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