Slide 1

Slide 1 text

Object Reorientation @mathiasverraes

Slide 2

Slide 2 text

Mathias Verraes Independent Consultant @mathiasverraes verraes.net

Slide 3

Slide 3 text

Systems are all about behaviour

Slide 4

Slide 4 text

State as a side effect

Slide 5

Slide 5 text

Functions f(x, y) -> z

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Types f(x, y) -> z where x : int y : int z : 1 | 0 | -1

Slide 9

Slide 9 text

Types function compare($left, $right) { if($left > $right) return 1; elseif($left == $right) return 0; else return -1; }

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Types are logical propositions about your code

Slide 12

Slide 12 text

Types make it easier for programs to reason about programs

Slide 13

Slide 13 text

Tools for typing in PHP: Classes interfaces type hints

Slide 14

Slide 14 text

Types prevent type bugs function send( $articleId, $subscriberId ) send($subscriberId, $articleId) // bug function send( ArticleId $articleId, SubscriberId $subscriberId )

Slide 15

Slide 15 text

Recurring pattern f(d : T, a, b) -> d' : T

Slide 16

Slide 16 text

Recurring pattern function doOperation($onData, $withArg, $andArg) { // ... return $newData; } function otherOp($onData, $withArg) { // ... return $newData; }

Slide 17

Slide 17 text

Encapsulation

Slide 18

Slide 18 text

$article = [ 'title' => "Object Reorientation", 'body' => "Encapsulate all the things", 'author' => "@mathiasverraes", 'published' => "no", ]; $article['published'] = "yes";

Slide 19

Slide 19 text

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, ];

Slide 20

Slide 20 text

That's not what arrays are for

Slide 21

Slide 21 text

Arrays are collections of values of the same type

Slide 22

Slide 22 text

NOT OK $article = [ 'title' => "Object Reorientation", 'body' => "Encapsulate all the things", 'author' => "@mathiasverraes", 'published' => false, ];

Slide 23

Slide 23 text

OK $fib = [1, 1, 2, 3, 5, 8, 13]; $articles = [ $objectReorientation, $understandingFibonacci, ]; $indexedArticles = [ 34 => $objectReorientation, 951 => $understandingFibonacci, ];

Slide 24

Slide 24 text

Encapsulation doOperation($onData, $withArg, $andArg) -> $object->doOperation($withArg, $andArg) $article->publish()

Slide 25

Slide 25 text

Design objects from the outside $article = new Article($title, $body, $author); $article->publish(); $this->setExpectedException( CantChangePublishedArticle::class ); $this->rename($newTitle); // throws!

Slide 26

Slide 26 text

Objects guard their own consistency $article = new Article(); // <-inconsistent $article->setTitle($title); // <-inconsistent $article->setBody($body); // <-inconsistent $article->setAuthor($author); // <-consistent

Slide 27

Slide 27 text

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; } }

Slide 28

Slide 28 text

Objects are contracts final class StoryUpdates { public function notify(Article $source, Subscriber $subscriber) { // ... } }

Slide 29

Slide 29 text

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; } }

Slide 30

Slide 30 text

Objects decompose final class TimeSlot { public function __construct( Time $startTime, Time $endTime ) { // ... } }

Slide 31

Slide 31 text

Object patterns

Slide 32

Slide 32 text

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; } }

Slide 33

Slide 33 text

No, you will not have too many objects.

Slide 34

Slide 34 text

Trust me on that.

Slide 35

Slide 35 text

Value Object final class Money { private $amount; private $currency; public function __construct($amount, Currency $currency) { // guards ... $this->amount = $amount; $this->currency = $currency; } }

Slide 36

Slide 36 text

attracts behaviour final class Money { /** @return Money **/ public function add(Money $other) /** @return Money[] **/ public function allocate([6, 3, 1]) }

Slide 37

Slide 37 text

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!

Slide 38

Slide 38 text

values objects are immutable $new_price = $jim_price->subtract($coupon);

Slide 39

Slide 39 text

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 ); }

Slide 40

Slide 40 text

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; } }

Slide 41

Slide 41 text

lifecycle changes over time stable identity

Slide 42

Slide 42 text

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) }

Slide 43

Slide 43 text

Procedural -> abstractions into objects

Slide 44

Slide 44 text

$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, ...); } }

Slide 45

Slide 45 text

$customer['id'] vs $customer->getId();

Slide 46

Slide 46 text

Specification final class CustomerHas5Orders { private $db; public function __construct(DB $db) { $this->db = $db; } /** @return bool */ public function isSatisfiedBy(Customer $customer) { // ... } }

Slide 47

Slide 47 text

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; } }

Slide 48

Slide 48 text

Polymorphism final class TenantAStrategy implements MailingTemplateStrategy { /* ... */} final class TenantBStrategy implements MailingTemplateStrategy { /* ... */}

Slide 49

Slide 49 text

Give up control $campaign = new PromotionCampagin( new TenantAStrategy() ); tell a story $campaign->sendNewsletterTo( $customers->registeredBefore($date) );

Slide 50

Slide 50 text

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, ...); } }

Slide 51

Slide 51 text

looks like business $campaign->sendNewsletterTo( $customers->registeredBefore($date) );

Slide 52

Slide 52 text

great programming is great Storytelling

Slide 53

Slide 53 text

verraes.net @mathiasverraes

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

inheritance is (mostly) evil