Sergey Protko
Some stuff
A module should be open for extension but closed for modification.
public function pay(Money $amount, string $method): Invoice
{
switch ($method) {
case 'VISA':
case 'MASTER_CARD':
return $this->creditCall->invoice($amount);
case 'BITCOIN':
return $this->generateBTCInvoice($amount);
case 'cash':
return new CashInvoice($amount);
}
}
Depend upon Abstractions. Do not depend upon concretions.
interface PaymentMethod
{
/**
* Return true if implementation supports
* this payment method
*/
public function supports(string $method): bool;
/**
* Generated invoice for given payment method
*/
public function pay(Money $amount): Invoice;
}
class User {}
class Customer extends User {}
class Merchant extends User
{
public function __construct()
{
parent::__construct();
// same as
// $this->user = new User();
}
}
class User {}
class Customer {}
class Merchant
{
public function __construct(User $user)
{
$this->user = $user;
}
}
Subtypes should be substitutable for their base types
abstract class Money
{
public function add(Money $amount): self;
public function substitute(Money $amount): self;
public function multiply(int $coef): self;
// ...
}
abstract class Money
{
abstract public function exchange(CurrencyConverter $converter): self;
public function add(Money $amount): self;
public function substitute(Money $amount): self;
public function multiply(int $coef): self;
// ...
}
class Bonus extends Money
{
public function exchange(CurrencyConverter $converter): self
{
throw new NotImplementedException(
"Bonuses cannon be converted back to money"
);
}
}
if ($amount instanceof Bonus) {
// handle bonus
} else {
// handle something other
}
Many client specific interfaces are better than one general purpose interface
interface Exchangeable
{
public function exchange(CurrencyConverter $converter): self;
}
Depend in the direction of stability.
Each module should have one and only one reason to change
What about all that talk about screwing up future events?
By Sergey Protko