Je suis le susnommé Kevin Nadin
Twitter : @kevinjhappy
Je travaille à Darkmira comme développeur PHP, c'est une boite avec pleins de dev cool comme toi, ou toi, ou...
non pas toi ;)
(bon, ça fait plus qu'un mot tout ça)
Exemple: je veux gérer un email, donc je fais une classe avec une donnée
<?php
class EmailAddress {
private $emailAddress;
}
J'ai mis ma propriété en privé volontairement, je rajoute un getter
<?php
class EmailAddress {
private $emailAddress;
public function toString()
{
return $this->emailAddress;
}
}
Et je passe par le constructeur pour....
ben construire mon objet :D
<?php
class EmailAddress {
private $emailAddress;
public function __construct(string $emailAddress)
{
$this->emailAddress = $emailAddress;
}
public function toString(): string
{
return $this->emailAddress;
}
}
<?php
class EmailAddress {
private $emailAddress;
public function __construct(string $emailAddress)
{
$this->emailAddress = $emailAddress;
}
public function toString(): string
{
return $this->emailAddress;
}
}
On applique ici un principe de Defensive Programming, les variables que l'on crée ne sont plus modifiables !
Il n'y a pas de setter dans ma classe, et le paramètre est privée, donc une fois crée en passant par le constructeur il n'est plus modifiable
On applique aussi un principe de DDD "Domain Driven Design", une variable string ne "signifie" rien alors qu'une classe "EmailAddress" est bien plus parlante dans le contexte dans lequel elle est utilisée !
On sait ici que l'on parle d'un e-mail, et pas d'un nom de famille par exemple.
On peut compléter le constructeur pour permettre de rajouter des conditions
<?php
class EmailAddress {
private $emailAddress;
public function __construct(string $emailAddress)
{
if (filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
throw new Exception('Erreur sur email');
}
$this->emailAddress = $emailAddress;
}
public function toString(): string {/*...*/}
}
Et sur la condition que nous avons ajouté, on lève une exception dédiée pour cette classe
<?php
class EmailAddress {
private $emailAddress;
public function __construct(string $emailAddress)
{
if (filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
throw new InvalidEmailAddressException($emailAddress);
}
$this->emailAddress = $emailAddress;
}
public function toString(): string {/*...*/}
}
class InvalidEmailAddressException extends LogicException {/*...*/}
Cette classe n'a pas à être étendue, on ferme la porte aux modifications par héritage
=> la classe passe en "final"
<?php
final class EmailAddress {
private $emailAddress;
//...
}
<?php
final class EmailAddress {
private $emailAddress;
public function __construct(string $emailAddress)
{
if (filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
throw new InvalidEmailAddressException($emailAddress);
}
$this->emailAddress = $emailAddress;
}
public function toString(): string
{
return $this->emailAddress;
}
}
<?php
class LoginService {
public function login(string $email, string $password) {
// TODO: vérifier l'email
// TODO: vérifier la validité du mot de passe
//...
}
}
<?php
class LoginService {
public function login(EmailAddress $email, VerifiedPassword $password) {
//...
}
}
Devient :
<?php
class LoginService {
public function login(EmailAddress $email, VerifiedPassword $password) {
//...
}
}
Une variable string pourrait laisser la place à tout et n'importe quoi, ici on sait que l'on passe un Email valide et un mot de passe vérifié
Si vous souhaitez rajouter une vérification supplémentaire sur les emails, c'est très facile car la vérification est centralisé.
(...oui c'est Français...)
Le Value Object peut être réutilisée facilement à tout moment où nous aurions besoin de manipuler un email
Parce que c'est important ;)
Les Value Object n'ont aucune dépendance envers aucune autre classe, ils se suffisent à eux-même
Cependant il est possible de faire des Value Object utilisant d'autres Value Object
<?php
final class DateTimeInFuture {
private $dateTime;
public function __construct(DateTime $dateTime)
{
if ($dateTime < new \DateTime()) {
throw new DateTimeNotInFutureException($dateTime);
}
$this->dateTime = $dateTime;
}
public function toDateTime(): DateTime
{
return $this->dateTime;
}
}
<?php
final class DateTimeRangeInFuture {
private $startDate;
private $endDate;
public function __construct(
DateTimeInFuture $startDate,
DateTimeInFuture $endDate
) {
if ($startDate->toDateTime() > $endDate->toDateTime()) {
throw new InvalidDateTimeRangeException($startDate, $endDate);
}
$this->startDate = $startDate;
$this->endDate = $endDate;
}
public function start(): DateTime
{
return $this->startDate->toDateTime();
}
public function end(): DateTime
{
return $this->endDate->toDateTime();
}
}
(j'aurai pu y penser tout seul d'abord ...)