SOLID é um acrônimo mnemônico criado por Michael Feathers e popularizados pelo Uncle Bob, Robert C. Martin, que relaciona a cada letra um tópico de boas práticas de programação. A prática destes princípios SOLID visa deixar o projeto mais coeso, reaproveitável e torna a sua manutenção mais fácil.
Este artigo abordará as duas primeiras letras – ou melhor, as duas primeiras práticas –, o Single responsibility principle (princípio da responsabilidade única) e o Open/closed principle (principio aberto/fechado).

O princípio da responsabilidade única tem como regra que uma classe deve possuir uma, e apenas uma, responsabilidade. Pode-se traduzir isso em “uma classe deve ter apenas um motivo para mudar”.
Sumário
Por que uma classe deve ter apenas uma responsabilidade?
A reposta é simples: quanto maior o número de responsabilidades, maiores as chances de modificação dessa classe no futuro e maiores as chances de inserção de bugs que atrapalharão a classe por inteiro.
Imaginemos o seguinte exemplo:
#region Código 1
public class Cozinheiro
{
public void fazerPrato(object prato)
{
// código para a realização de pratos
}
public double valorDoPrato(object prato)
{
// código para o calculo do valor do prato
}
public void recolherPratos()
{
// código para recolher os pratos
}
}
#endregion
Esta classe é responsável por preparar o prato e calcular o valor do prato e recolher os pratos. Esses ‘es’ indicam que a nossa classe possui mais de uma responsabilidade, e assim, mais de um motivo para ser modificada. Caso desejássemos modificar a maneira com que os pratos são recolhidos, teríamos que modificar a nossa classe Cozinheiro. Caso esta modificação gerasse algum bug, não apenas a retirada dos pratos, mas sim todas as funcionalidades da classe poderiam estar comprometidas. A classe Cozinheiro deixaria de preparar a comida devido um problema com a retirada de pratos.
Como deveria ser?
A pergunta aqui é: qual a responsabilidade da classe Cozinheiro? Preparar o prato! Perceba que não importam a quantidade de métodos necessários para tal. A ‘responsabilidade única’ está ligada à função da classe, e não à maneira que esta função é implementada. Veja como ficaria a refatoração do código:
#region Código 2
public class Cozinheiro
{
public void fazerPrato(object prato)
{
// código para a realização de pratos
}
}
public class Caixa
{
public double valorDoPrato(object prato)
{
// código para o calculo do valor do prato
}
}
public class Garcon
{
public void recolherPratos()
{
// código para recolher os pratos
}
}
#endregion
A classe Cozinheiro agora possui apenas uma única responsabilidade: preparar o prato. O único motivo para alterar essa classe é caso a forma de preparar o prato seja modificada, reduzindo assim a complexidade, facilitando a legibilidade e reduzindo o acoplamento.

O princípio aberto/fechado diz que as entidades de software (classes, módulos, funções, etc.) devem estar abertas para extensão, mas fechadas para modificação, ou seja, deve ser possível estender o comportamento de uma classe, mas não a modificar.
Qual a importância deste princípio?
A extensibilidade! É comum a manutenção de códigos. Você deve, então, lidar com as classes já criadas e, possivelmente, adicionar ou remover funcionalidades. Para uma classe ‘não-extensível’, a adição ou remoção de funcionalidades implica na modificação da classe por si só. Ao fazer tal mudança, você estará propenso a fazer com que outras partes do seu código não funcionem mais.
Utilizando a nossa classe Cozinheiro, poderíamos implementar a função de preparar pratos da seguinte forma:
#region Código 3
public class Cozinheiro
{
public void fazerPrato(object prato)
{
if (prato is Omelete)
{
// código para a realização de um omelete
}
if (prato is Macarrao)
{
// código para a realização de um macarrao
}
if (prato is FrangoAssado)
{
// código para a realização de um frango assado
}
}
}
public class Omelete
{
// propriedades do omelete
}
public class Macarrao
{
// propriedades do macarrão
}
public class FrangoAssado
{
// propriedades do frango assado
}
#endregion
Aparentemente, nenhum problema. Mas e se quiséssemos implementar um prato novo? Teríamos que modificar a classe Cozinheiro.
E qual é o problema de um IF a mais na classe?
Ao adicionar outro IF de validação, além de termos que substituir a classe na publicação da nova versão, estaremos correndo o risco de introduzir bugs em uma classe que já estava funcionando.
Assim, para cada prato novo, seriam necessárias novas alterações na classe Cozinheiro, e para cada uma dessas alterações, teríamos que nos assegurar que as funcionalidades anteriores continuassem funcionando e que as novas funcionalidades não interferissem nas antigas.
Então qual é a solução?
A utilização da abstração. A classe pai deve possuir o método exigido (que no nosso caso é o fazerPrato), enquanto os filhos dessa classe devem apenas implementar a forma com que este método será executado. Veja como este princípio seria implementado no exemplo anterior:
#region Código 4
public class Cozinheiro
{
public void fazerPrato(Prato prato)
{
prato.Fazer();
}
}
public abstract class Prato
{
public abstract void Fazer();
}
public class Omelete : Prato
{
// propriedades do omelete
public override void Fazer()
{
// código para a realização de um omelete
}
}
public class Macarrao : Prato
{
// propriedades do macarrao
public override void Fazer()
{
// código para a realização de um macarrao
}
}
public class FrangoAssado : Prato
{
// propriedades do frango assado
public override void Fazer()
{
// código para a realização de um frango assado
}
}
#endregion
Possuímos agora uma abstração bem definida, onde todas as extensões implementam suas próprias regras de negócio sem necessidade de modificar uma funcionalidade devido a remoção, mudança ou inclusão de outra.
Dessa forma a nossa nova classe Cozinheiro está aberta para extensão pois podemos incluir tantos pratos quanto desejarmos, mas está fechada para modificação pois podemos fazer isso sem ter que alterar a classe. Assim, além da independência entre as extensões, o código fica mais claro e a aplicação de testes de unidades fica mais fácil e coesa.
Observe assim que aplicando apenas dois princípios do SOLID em conjunto, aumenta-se bastante as chances de que o sistema criado seja mais claro e simples de estender.