SOLID é um acrônimo de 5 princípios da programação orientada a objetos, são eles:
- Single Responsability Principle (Princípio da responsabilidade única)
- Open/Closed Principle (Princípio aberto/fechado)
- Liskov Substitution Principle (Princípio da substituição de Liskov)
- Interface Segregation Principle (Princípio da segregação da interface)
- Dependency Inversion Principle (Princípio da inversão da dependência)
Sua classe não deve ter mais de uma responsabilidade
Uma classe deve ter uma e apenas uma razão para mudança, significando que uma classe deve ter apenas uma responsabilidade.
Esse princípio declara que uma classe deve ser especializada em um único assunto e possuir apenas uma responsabilidade dentro do software, ou seja, a classe deve ter uma única tarefa ou ação para executar.
- Exemplo em PHP
/** Exemplo que não segue o princípio */
class UserRegistrationController
{
public function register(Request $request, Response $response)
{
$user = $this->createUser($request);
$this->database->query("insert into users ...");
$this->email
->to($user->email())
->subject("Welcome")
->send();
return $this->successfulResponse($response)
}
}
/** Exemplo que segue o princípio */
class UserRegistration
{
public function __construct(RegistrationStorage $storage, RegistrationEmail $email)
{
$this->storage = $storage;
$this->email = $email;
}
public function register(User $user)
{
$this->storage->save($user);
$this->email->sendTo($user);
}
}
- Objetivo: Este princípio visa separar comportamentos para que, se surgirem bugs como resultado de sua mudança, isso não afete outros comportamentos não relacionados.
Objetos ou entidades devem ser abertas para extensão, mas fechadas para modificação.
- Exemplo em PHP
/** Exemplo que não segue o princípio */
abstract class Duck
{
public function swim(){...}
public function quack(){...}
public function fly(){...}
}
class CityDuck extends Duck {...}
class WildDuck extends Duck {...}
class RubberDuck extends Duck {...}
class WoodenDuck extends Duck {...}
/** Exemplo que segue o princípio */
interface Swimming { public function swim(); }
interface Quacking { public function makeNoise(); }
interface Flying { public function fly(); }
class WildDuck implements Quacking
{
public function makeNoise()
{
$this->quacking->makeNoise();
}
}
class RubberDuck
{
function __construct (Swimming $swimming, Quacking $quacking, Flying $flying)
{
$this->swimming = $swimming;
$this->quacking = $quacking;
$this->flying = $flying;
}
public function quack ()
{
$this->quacking->makeNoise();
}
}
class Squeaking implements Quacking ()
{
public function makeNoise(){}
}
New RubberDuck(new Swimming, new Squeaking, new Flying);
- Objetivo: Este princípio visa estender o comportamento de uma classe sem alterar o comportamento existente dessa classe. Isso evita causar bugs onde quer que a classe esteja sendo usada.
Uma classe derivada deve ser substituída por sua classe base.
Este princípio tem este nome porque foi introduzido por Barbara Liskov em sua apresentação "Data Abstraction" em 1987. Alguns anos mais tarde, ela publicou um artigo, junto com Jeanette Wing, onde elas definiram o princípio como
Seja Φ(x) uma provável propriedade sobre objetos x do tipo T. Então Φ(y) deve ser verdadeira para objetos y do tipo S, em que S é um subtipo de T.
- Exemplo em PHP
/** Exemplo que não segue o princípio*/
<?php
class Rectangle
{
protected $height;
protected $width;
public function setHeight(int $height)
{
$this->height = $height;
}
public function setWidth(int $width)
{
$this->width = $width;
}
}
class Square extends Rectangle
{
public function setHeight(int $height)
{
$this->height = $height;
$this->width = $height;
}
public function setWidth(int $width)
{
$this->height = $width;
$this->width = $width;
}
}
/** Exemplo que segue o princípio*/
<?php
abstract class AbstractShape
{
abstract public function Area() : int;
}
class Rectangle extends AbstractShape
{
private $height;
private $width;
public function setHeight(int $height)
{
$this->height = $height;
}
public function setWidth(int $width)
{
$this->width = $width;
}
public function Area() : int
{
return $this->height * $this->width;
}
}
class Square extends AbstractShape
{
private $sideLength;
public function setSideLength(int $sideLength)
{
$this->sideLength = $sideLength;
}
public function Area() : int
{
return $this->sideLength * $this->sideLength;
}
}
- Objetivo: Este princípio visa reforçar a consistência para que a classe pai ou sua classe filha possam ser usadas da mesma maneira sem erros.
Os clientes não devem ser forçados a depender de métodos que não usam.
- Exemplo em PHP
/** Exemplo que não segue o princípio */
<?php
interface Bird
{
public function eat();
public function fly();
}
/** Classe de uma andorinha */
class Swallow implements Bird
{
public function eat() { ... }
public function fly() { ... }
}
/** Classe de um Avestruz **/
class Ostrich implements Bird
{
public function eat() { ... }
public function fly() { /* exception */ }
}
/** Exemplo que segue o princípio */
<?php
interface Bird
{
public function eat();
}
interface FlyingBird
{
public function fly();
}
interface RunningBird
{
public function run();
}
class Swallow implements Bird, FlyingBird
{
public function eat() { ... }
public function fly() { ... }
}
class Ostrich implements Bird, RunningBird
{
public function eat() { ... }
public function run() { ... }
}
- Objetivo: Este princípio visa dividir um conjunto de ações em conjuntos menores, de forma que uma Classe execute SOMENTE o conjunto de ações de que necessita.
Uma classe não deve ser misturada com a ferramenta que usa para executar uma ação. Em vez disso, deve ser implementada na interface que permitirá que a ferramenta se conecte à classe
- Exemplo em PHP
/** Exemplo que não segue o princípio */
<?php
use MySQLConnection;
class PasswordReminder
{
private $dbConnection;
public function __construct(MySQLConnection $dbConnection)
{
$this->dbConnection = $dbConnection;
}
// Faz alguma coisa
}
/** Exemplo que segue o princípio*/
<?php
interface DBConnectionInterface
{
public function connect();
}
class MySQLConnection implements DBConnectionInterface
{
public function connect()
{
// ...
}
}
class OracleConnection implements DBConnectionInterface
{
public function connect()
{
// ...
}
}
class PasswordReminder
{
private $dbConnection;
public function __construct(DBConnectionInterface $dbConnection) {
$this->dbConnection = $dbConnection;
}
// Faz alguma coisa
}
- Objetivo: Este princípio visa reduzir a dependência de uma classe de alto nível na classe de baixo nível, introduzindo uma interface.
Basta criar um fork do projeto, realizar as modificações que achar necessário e depois fazer um Pull Request. Toda ajuda é bem vinda, caso veja algum erro, não hesite em contribuir com o projeto!
Conteúdos em português:
Artigos:
- SOLID com PHP
- Princípios SOLID de design para JavaScript
- O que é SOLID: O guia completo para você entender os 5 princípios da POO
Vídeos:
- Princípios SOLID em uma API REST com Node.js e TypeScript
- SOLID (O básico para você programar melhor)
- SOLID fica FÁCIL com Essas Ilustrações
Conteúdos em inglês:
Artigos:
- The Principles of OOD
- SOLID Principles In PHP
- The S.O.L.I.D Principles in Pictures
- S.O.L.I.D The first 5 principles of Object Oriented Design with JavaScript
- SRP: The Single Responsibility Principle
- The Open-Closed Principle
- The Liskov Substitution Principle
- The Interface Segregation Principle
- The Dependency Inversion Principle