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

The true value of objects

The true value of objects

Although the latest PHP versions provide us with enough types and keywords to enable object oriented development, the language itself is not truly object oriented. In pure object oriented languages, like Java, almost everything is an object. Even primitives have their object equivalent. Concepts that first look like simple values, can in fact be modelled as objects. This enables us to add a lot of behaviour to them. These so called Value Objects make our code more readable, elegant, maintainable and dry. We will explore the possibilities and advantages of these Value Objects together, guided by some real world code samples.

Stijn Vannieuwenhuyse

June 26, 2015
Tweet

More Decks by Stijn Vannieuwenhuyse

Other Decks in Programming

Transcript

  1. The true value of
    objects

    View full-size slide

  2. Stijn Vannieuwenhuyse
    @stijnvnh

    View full-size slide

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

    View full-size slide

  4. The true value of
    objects

    View full-size slide

  5. what are
    Objects?

    View full-size slide

  6. represent domain concepts
    encapsulate data
    expose behaviour

    View full-size slide

  7. Domain
    Driven
    Design

    View full-size slide

  8. Service
    Objects

    View full-size slide

  9. Value
    Objects

    View full-size slide

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

    View full-size slide

  11. $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

    View full-size slide

  12. Model domain
    concepts

    View full-size slide

  13. $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
    }

    View full-size slide

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

    View full-size slide

  15. $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
    }

    View full-size slide

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

    View full-size slide

  17. Achieve single
    responsability

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. 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

    View full-size slide

  21. Value objects should
    be immutable

    View full-size slide

  22. 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

    View full-size slide

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

    View full-size slide

  24. ENDLESS POSSIBILITIES

    View full-size slide

  25. 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?

    View full-size slide

  26. 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

    View full-size slide

  27. 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])
    )
    }
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  31. 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

    View full-size slide

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

    View full-size slide

  33. 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
    }

    View full-size slide

  34. 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) { /* ... */ }
    }

    View full-size slide

  35. what about database
    storage?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  39. what about memory usage?

    View full-size slide

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

    View full-size slide

  41. $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];
    }
    }

    View full-size slide

  42. Value object
    or entity?

    View full-size slide

  43. Blog Post
    Category

    View full-size slide

  44. VALUE OBJECTS
    represent domain values
    encapsulate data
    expose behaviour

    View full-size slide

  45. VALUE OBJECTS
    dry
    readability
    immutable

    View full-size slide

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

    View full-size slide