SOLID CODE

SOLID

It's all about dependencies

The Open/Close Principle

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

Why we change
our code?

Dependency Inversion

Principle

Depend upon Abstractions. Do not depend upon concretions.

Dependency Inversion

Principle

Dependency Inversion

Principle

Dependency Inversion

Principle

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

Dependency Inversion

Principle

Patterns

  • Strategy
  • State
  • Chain of Responsibility
  • Visitor
  • Decorator
  • Composition
  • Factory
  • ...

Inheritance and open/close

    
  class User {}
  class Customer extends User {}
  class Merchant extends User 
  {
      public function __construct()
      {
          parent::__construct();
          // same as
          // $this->user = new User();
      }
  }

Inheritance and open/close

    
  class User {}
  class Customer {}
  class Merchant 
  {
      public function __construct(User $user)
      {
          $this->user = $user;
      }
  }

Inheritance and open/close

The Liskov Substitution Principle

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

Money

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

Money Exchange

class Bonus extends Money
{
    public function exchange(CurrencyConverter $converter): self
    {
        throw new NotImplementedException(
            "Bonuses cannon be converted back to money"
        );
    }
}

Money Exchange

  
  if ($amount instanceof Bonus) {
     // handle bonus
  } else {
   // handle something other
  }

LSP Violations
Symptoms

Interface Segregation Principle

Many client specific interfaces are better than one general purpose interface

For those who needs exchanges 

 
 interface Exchangeable
 {
     public function exchange(CurrencyConverter $converter): self;
 }

The Stable Dependencies Principle

Depend in the direction of stability.

Stability is expressed

by the frequency of changes in behavior

Single Responsibility Principle

Each module should have one and only one reason to change

Single Responsibility Principle

Single Responsibility Principle

Reason for change

What about all that talk about screwing up future events?

Refactoring?

GRASP

General responsibility assignment software patterns

Imagine my voice

When he answers your questions