SOLID é uma sigla para os primeiros cinco princípios do design orientado a objeto (OOD) criada por Robert C. Martin (também conhecido como Uncle Bob).
Nota: embora esses princípios sejam aplicáveis a várias linguagens de programação, o código de amostra contido neste artigo usará o PHP.
Esses princípios estabelecem práticas que contribuem para o desenvolvimento de software com considerações de manutenção e extensão à medida que o projeto cresce. A adoção dessas práticas também pode contribuir para evitar problemas de código, refatoração de código e o desenvolvimento ágil e adaptativo de software.
SOLID significa:
Neste artigo, cada princípio será apresentado individualmente para que você compreenda como o SOLID pode ajudá-lo(a) a melhorar como desenvolvedor(a).
O Princípio da responsabilidade única (SRP) declara:
Uma classe deve ter um e apenas um motivo para mudar, o que significa que uma classe deve ter apenas uma função.
Por exemplo, considere um aplicativo que recebe uma coleção de formas — círculos e quadrados — e calcula a soma da área de todas as formas na coleção.
Primeiramente, crie as classes de formas e faça com que os construtores configurem os parâmetros necessários.
Para quadrados, será necessário saber o length
(comprimento) de um lado:
Para os círculos, será necessário saber o radius
(raio):
Em seguida, crie a classe AreaCalculator
e então escreva a lógica para somar as áreas de todas as formas fornecidas. A área de um quadrado é calculada pelo quadrado do comprimento. A área de um círculo é calculada por pi multiplicado pelo quadrado do raio.
Para usar a classe AreaCalculator
, será necessário criar uma instância da classe, passar uma matriz de formas e exibir o resultado no final da página.
Aqui está um exemplo com uma coleção de três formas:
O problema com o método de saída é que o AreaCalculator
manuseia a lógica para gerar os dados.
Considere um cenário onde o resultado deve ser convertido em outro formato, como o JSON.
Toda a lógica seria manuseada pela classe AreaCalculator
. Isso violaria o princípio da responsabilidade única. A classe AreaCalculator
deve estar preocupada somente com a soma das áreas das formas fornecidas. Ela não deve se importar se o usuário quer JSON ou HTML.
Para resolver isso, crie uma classe separada chamada SumCalculatorOutputter
e use essa nova classe para lidar com a lógica necessária para gerar os dados para o usuário:
A classe SumCalculatorOutputter
funcionaria da seguinte forma:
Agora, a lógica necessária para gerar os dados para o usuário é manuseada pela classe SumCalculatorOutputter
.
Isso satisfaz o princípio da responsabilidade única.
O Princípio do aberto-fechado (S.R.P.) declara:
Os objetos ou entidades devem estar abertos para extensão, mas fechados para modificação.
Isso significa que uma classe deve ser extensível sem que seja modificada.
Vamos revisitar a classe AreaCalculator
e focar no método sum
(soma):
Considere um cenário onde o usuário deseja a sum
de formas adicionais, como triângulos, pentágonos, hexágonos, etc. Seria necessário editar constantemente este arquivo e adicionar mais blocos de if
/else
. Isso violaria o princípio do aberto-fechado.
Uma maneira de tornar esse método sum
melhor é remover a lógica para calcular a área de cada forma do método da classe AreaCalculator
e anexá-la à classe de cada forma.
Aqui está o método area
definido em Square
:
E aqui está o método area
definido em Circle
:
O método sum
para AreaCalculator
pode então ser reescrito como:
Agora, é possível criar outra classe de formas e a passar ao calcular a soma sem quebrar o código.
No entanto, outro problema surge. Como saber que o objeto passado para o AreaCalculator
é na verdade uma forma ou se a forma possui um método chamado area
?
Programar em uma interface é uma parte integral do SOLID.
Crie uma ShapeInterface
que suporte area
:
Modifique suas classes de formas para implement
(implementar) a ShapeInterface
.
Aqui está a atualização para Square
:
E aqui está a atualização para Circle
:
No método sum
para AreaCalculator
, verifique se as formas fornecidas são na verdade instâncias de ShapeInterface
; caso contrário, lance uma exceção:
Isso satisfaz o princípio do aberto-fechado.
O Princípio da substituição de Liskov declara:
Seja q(x) uma propriedade demonstrável sobre objetos de x do tipo T. Então q(y) deve ser demonstrável para objetos y do tipo S onde S é um subtipo de T.
Isso significa que cada subclasse ou classe derivada deve ser substituível pela classe sua classe base ou pai.
Analisando novamente a classe de exemplo AreaCalculator
, considere uma nova classe VolumeCalculator
que estende a classe AreaCalculator
:
Lembre-se que a classe SumCalculatorOutputter
se assemelha a isto:
Se você tentar executar um exemplo como este:
Quando chamar o método HTML
no objeto $output2
, você irá obter um erro E_NOTICE
informando uma conversão de matriz em string.
Para corrigir isso, em vez de retornar uma matriz do método de soma de classe VolumeCalculator
, retorne $summedData
:
O $summedData
pode ser um float, duplo ou inteiro.
Isso satisfaz o princípio da substituição de Liskov.
O Princípio da segregação de interfaces declara:
Um cliente nunca deve ser forçado a implementar uma interface que ele não usa, ou os clientes não devem ser forçados a depender de métodos que não usam.
Ainda utilizando o exemplo anterior do ShapeInterface
, você precisará suportar as novas formas tridimensionais Cuboid
e Spheroid
, e essas formas também precisarão ter o volume
calculado.
Vamos considerar o que aconteceria se você modificasse a ShapeInterface
para adicionar outro contrato:
Agora, qualquer forma criada deve implementar o método volume
, mas você sabe que os quadrados são formas planas que não têm volume, de modo que essa interface forçaria a classe Square
a implementar um método sem utilidade para ela.
Isso violaria o princípio da segregação de interfaces. Ao invés disso, você poderia criar outra interface chamada ThreeDimensionalShapeInterface
que possui o contrato volume
e as formas tridimensionais poderiam implementar essa interface:
Essa é uma abordagem muito mais vantajosa, mas uma armadilha a ser observada é quando sugerir o tipo dessas interfaces. Ao invés de usar uma ShapeInterface
ou uma ThreeDimensionalShapeInterface
, você pode criar outra interface, talvez ManageShapeInterface
, e implementá-la tanto nas formas planas quanto tridimensionais.
Dessa forma, é possível ter uma única API para gerenciar todas as formas:
Agora, na classe AreaCalculator
, substitua a chamada do método area
por calculate
e verifique se o objeto é uma instância da ManageShapeInterface
e não da ShapeInterface
.
Isso satisfaz o princípio da segregação de interfaces.
O princípio da inversão de dependência declara:
As entidades devem depender de abstrações, não de implementações. Ele declara que o módulo de alto nível não deve depender do módulo de baixo nível, mas devem depender de abstrações.
Esse princípio permite a desestruturação.
Aqui está um exemplo de um PasswordReminder
que se conecta a um banco de dados MySQL:
Primeiramente, o MySQLConnection
é o módulo de baixo nível, enquanto o PasswordReminder
é de alto nível. No entanto, de acordo com a definição de D em SOLID, que declara Dependa de abstrações e não de implementações, Esse trecho de código acima viola esse princípio, uma vez que a classe PasswordReminder
está sendo forçada a depender da classe MySQLConnection
.
Mais tarde, se você alterasse o mecanismo do banco de dados, também teria que editar a classe PasswordReminder
e isso violaria o princípio do aberto-fechado.
A classe PasswordReminder
não deve se importar com qual banco de dados seu aplicativo usa. Para resolver esses problemas, programe em uma interface, uma vez que os módulos de alto e baixo nível devem depender de abstrações:
A interface possui um método de conexão e a classe MySQLConnection
implementa essa interface. Além disso, em vez de sugerir o tipo diretamente da classe MySQLConnection
no construtor do PasswordReminder
, você sugere o tipo de DBConnectionInterface
. Sendo assim, independentemente do tipo de banco de dados que seu aplicativo usa, a classe PasswordReminder
poderá se conectar ao banco de dados sem problemas e o princípio do aberto-fechado não será violado.
Esse código estabelece que tanto os módulos de alto quanto de baixo nível dependem de abstrações.
Neste artigo, os cinco princípios do Código SOLID foram-lhe apresentados. Projetos que aderem aos princípios SOLID podem ser compartilhados com colaboradores, estendidos, modificados, testados e refatorados com menos complicações.
Continue seu aprendizado lendo sobre outras práticas para o desenvolvimento de software ágil e adaptativo.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!