Slide 1

Slide 1 text

The true value of objects

Slide 2

Slide 2 text

Stijn Vannieuwenhuyse @stijnvnh

Slide 3

Slide 3 text

ONE Agency - Ausy Belgium Team lead DDD, CQRS, event sourcing

Slide 4

Slide 4 text

The true value of objects

Slide 5

Slide 5 text

what are Objects?

Slide 6

Slide 6 text

represent domain concepts encapsulate data expose behaviour

Slide 7

Slide 7 text

SOLID

Slide 8

Slide 8 text

Domain Driven Design

Slide 9

Slide 9 text

Service Objects

Slide 10

Slide 10 text

Entities

Slide 11

Slide 11 text

Value Objects

Slide 12

Slide 12 text

class Money { private $amount; private $currency; public function __construct($amount, $currency) { $this->amount = $amount; $this->currency = $currency; } public function getAmount() { return $this->amount; } public function getCurrency() { return $this->currency; } }

Slide 13

Slide 13 text

$a = new Money(5.00, "EUR"); $b = new Money(5.00, "EUR"); $c = new Money(4.23, "EUR"); $d = new Money(5.00, "USD"); $a === $b; //false $a == $b; //true $a == $c; //false $a == $d; //false

Slide 14

Slide 14 text

but ... Why?

Slide 15

Slide 15 text

Model domain concepts

Slide 16

Slide 16 text

$amsterdam = new City( "Amsterdam", "Netherlands", 4.8951679, 52.3702157 ); class City { private $latitude; private $longitude; //other fields public function __construct( $name, $country, $latitude, $longitude ) { $this->latitude = $latitude; $this->longitude = $longitude; //other assignments } //getters }

Slide 17

Slide 17 text

class Coordinate { private $latitude; private $longitude; public function __construct($latitude, $longitude) { $this->latitude = $latitude; $this->longitude = $longitude; } //getters }

Slide 18

Slide 18 text

$coordinate = new Coordinate(52.3702157, 4.8951679); $amsterdam = new City("Amsterdam", "Netherlands", $coordinate); class City { /** * @var Coordinate */ private $coordinate; //other fields public function __construct( $name, $country, Coordinate $coordinate ) { $this->coordinate = $coordinate; //other assignments } //getters }

Slide 19

Slide 19 text

Avoid bugs

Slide 20

Slide 20 text

$amsterdam = new City( "Amsterdam", "Netherlands", 4.8951679, 52.3702157 ); class City { public function __construct( $name, $country, $latitude, $longitude ) { //... } }

Slide 21

Slide 21 text

Achieve single responsability

Slide 22

Slide 22 text

class HexColor { private $hexColor; public function __construct($hexColor) { if(!preg_match('/^#?[a-f0-9]{6}$/i', $hexColor)) { throw new \InvalidArgumentException(); } $this->hexColor = $hexColor; } public function blend(HexColor $otherHexColor) { $newHexColor = // color blending...; return new HexColor($newHexColor); } }

Slide 23

Slide 23 text

PHP BUILT IN

Slide 24

Slide 24 text

$a = new DateTime("2015-06-26"); $b = $a->modify("+1 day"); $a == new DateTime("2015-06-26"); //false

Slide 25

Slide 25 text

class User { /** * @var DateTime */ private $registrationDate; public function __construct(DateTime $registrationDate) { $this->registrationDate = $registrationDate; } public function getRegistrationDate() { return $this->registrationDate; } } $user = new User(new DateTime("2015-06-26")); if($user->getRegistrationDate()->modify("+1 year") == new DateTime("today")) { //send anniversary gift } $user->getRegistrationDate() == new DateTime("2015-06-26"); //false $user->getRegistrationDate() == new DateTime("2016-06-26"); //true

Slide 26

Slide 26 text

CAUTION

Slide 27

Slide 27 text

Value objects should be immutable

Slide 28

Slide 28 text

class User { /** * @var DateTimeImmutable */ private $registrationDate; public function __construct(DateTimeImmutable $registrationDate) { $this->registrationDate = $registrationDate; } public function getRegistrationDate() { return $this->registrationDate; } } $user = new User(new DateTimeImmutable("2015-06-26")); if($user->getRegistrationDate()->modify("+1 year") == new DateTimeImmutable("today")) { //send anniversary gift } $user->getRegistrationDate() == new DateTimeImmutable("2015-06-26"); //true

Slide 29

Slide 29 text

class Money { //fields //constructor //getters public function add(Money $other) { if($this->currency != $other->currency) { throw new CurrenciesDontMatch(); } return new Money($this->amount + $other->amount, $this->currency); } }

Slide 30

Slide 30 text

ENDLESS POSSIBILITIES

Slide 31

Slide 31 text

class DateRange { private $start; private $end; public function __construct( DateTimeImmutable $start, DateTimeImmutable $end ) { if($start > $end) { throw new InvalidArgumentException(); } $this->start = $start; $this->end = $end; } //getters } $a = new DateRange(new DateTimeImmutable("today"), new DateTimeImmutable("tomorrow")); $b = new DateRange(new DateTimeImmutable("today"), new DateTimeImmutable("tomorrow")); $c = new DateRange(new DateTimeImmutable("yesterday"), new DateTimeImmutable("today")); $a == $b; //still true $a < $c; //will it work?

Slide 32

Slide 32 text

class DateRange { private $start; private $end; //constructor //getters /** * @return bool */ public function isLessThan(DateRange $other) { return $this->start < $other->start; } } $a = new DateRange(new DateTimeImmutable("today"), new DateTimeImmutable("tomorrow")); $b = new DateRange(new DateTimeImmutable("today"), new DateTimeImmutable("tomorrow")); $c = new DateRange(new DateTimeImmutable("yesterday"), new DateTimeImmutable("today")); $a->isLessThen($b); //false $a->isLessThan($c); //true

Slide 33

Slide 33 text

class DateRange { private $start; private $end; //constructor //getters //comparation public function __toString() { return $this->start->format("U") . " " . $this->end->format("U"); } public static function fromString($string) { if(!preg_match("/^(\S+) (\S+)$/", string, $matches)) { throw new InvalidArgumentException(); } return new DateRange( new DateTimeImmutable($matches[1]), new DateTimeImmutable($matches[2]) ) } }

Slide 34

Slide 34 text

class DateRange { //... /** * @return DateRange[] */ public static function summarize(array $dateRanges) { //some magic to combine overlapping dateranges return $dateRanges; } }

Slide 35

Slide 35 text

class PersonName { private $firstName; private $nickName; private $lastName; public function __construct($firstName, $lastName, $nickName = '') { $this->firstName = $firstName; $this->lastName = $lastName; $this->nickName = $nickName; } //getters public function getDisplayShortName() { return $this->nickName ?: $this->firstName; } public function getDisplayFullName() { return $this->getDisplayShortName() . " " . $this->lastName; } }

Slide 36

Slide 36 text

$string = " FooBar "; trim($string); //FooBar substr($string, 0, 2); //Fo strlen($string); // 2 strtolower($string); //fo //...

Slide 37

Slide 37 text

class String { public function __construct($string) { /*...*/ } public function length() { /*...*/ } public function substring($start, $length) { /*...*/ } public function toLowercase() { /*...*/ } public function trim() { /*...*/ } } $string = new String("foobar"); $string ->trim() ->substring(0, 2) ->toLowercase(); //fo

Slide 38

Slide 38 text

$weeklyTimeSlot = new WeeklyTimeSlot( WeekDay::friday(), new TimeSlot( new Time(16, 45) new Time(17, 30) ) ); /** @var duration Duration */ $duration = $weeklyTimeSlot->getDuration();

Slide 39

Slide 39 text

class SchoolYear { private $startYear; public function __construct($startYear) { if(!is_int($startYear)) { throw new InvalidArgumentException(); } $this->startYear = $startYear; } public function getStartYear() { /* ... */ } public function getEndYear() { /* ... */ } public function __toString() { /* ... */ } //2014-2015 }

Slide 40

Slide 40 text

class SchoolYear { private $startYear; public function __construct($startYear) { /* ... */ } public static function current() { /* ... */ } public static function containingDate(DateTimeImmutable $date) { /* ... */ } public static function fromString($string) { /* ... */ } public function next() { /* ... */ } public function previous() { /* ... */ } public function getStartYear() { /* ... */ } public function getEndYear() { /* ... */ } public function __toString() { /* ... */ } public function equals(SchoolYear $schoolYear) { /* ... */ } public function getStartDate() { /* ... */ } public function getEndDate() { /* ... */ } public function getDateRange() { /* ... */ } public function getCalendarWeeks() { /* ... */ } public function startsInWeekend() { /* ... */ } public function isActive() { /* ... */ } public function isPassed() { /* ... */ } public function containsCalendarWeek(CalendarWeek $calendarWeek) { /* ... */ } public function containsDate(DateTimeImmutable $date) { /* ... */ } public function containsDateRange(DateRange $date) { /* ... */ } }

Slide 41

Slide 41 text

But ...

Slide 42

Slide 42 text

what about database storage?

Slide 43

Slide 43 text

class Talk { //... /** * @ORM\Column(type="datetime") */ private $startTime; /** * @ORM\Column(type="datetime") */ private $endTime; public function __construct(/*...*/, TimeSlot $slot) { $this->startTime = $slot->getStartTime(); $this->endTime = $slot->getEndTime(); } /** * @return TimeSlot */ public function getTimeSlot() { return new TimeSlot($this->startTime, $this->endTime); } }

Slide 44

Slide 44 text

/** * @ORM\Embeddable */ class TimeSlot { /** * @ORM\Column(type="datetime") */ private $startTime; /** * @ORM\Column(type="datetime") */ private $endTime; //... }

Slide 45

Slide 45 text

class Talk { //... /** * @ORM\Embedded(class="TimeSlot") */ private $timeSlot public function __construct(/*...*/, TimeSlot $slot) { $this->timeSlot = $timeSlot; } /** * @return TimeSlot */ public function getTimeSlot() { return $this->timeSlot; } //... }

Slide 46

Slide 46 text

what about memory usage?

Slide 47

Slide 47 text

$cars = []; for($i = 0; $i <= 1000000; $i++) { $cars[] = new Car("Audi A6", "VIN-NR-2015" . $i, new HexColor("FF0000")); }

Slide 48

Slide 48 text

$cars = []; for($i = 0; $i <= 1000000; $i++) { $cars[] = new Car("Audi A6", "VIN-NR-2015" . $i, HexColor::define("FF0000")); } class HexColor { private $hexColor; private static $hexColors = []; private function __construct($hexColor) { /*...*/ } public static function define($hexColor) { if(!array_key_exists($hexColor, self::$hexColors)) { self::$hexColors[$hexColor] = new self($hexColor); } return self::$hexColors[$hexColor]; } }

Slide 49

Slide 49 text

Value object or entity?

Slide 50

Slide 50 text

User

Slide 51

Slide 51 text

Username

Slide 52

Slide 52 text

Product-id

Slide 53

Slide 53 text

Date

Slide 54

Slide 54 text

Money

Slide 55

Slide 55 text

Blog Post

Slide 56

Slide 56 text

Blog Post Category

Slide 57

Slide 57 text

VALUE OBJECTS represent domain values encapsulate data expose behaviour

Slide 58

Slide 58 text

VALUE OBJECTS dry readability immutable

Slide 59

Slide 59 text

QUESTIONS?

Slide 60

Slide 60 text

Thank you Stijn Vannieuwenhuyse - @stijnvnh https://joind.in/14229

Slide 61

Slide 61 text

No content