DEV Community

Cover image for SOLID
Victor Lis Bronzo
Victor Lis Bronzo

Posted on

SOLID

O QUE É O SOLID?

Se você é dev e já navegou aqui na rede, provavelmente já viu dezenas de posts sobre o assunto...

Então, sendo direto, SOLID é o conjunto de 5 princípios que ajudam na melhoria da qualidade, escalabilidade e manutenção de projetos de software. E aqui na rede, vou trazer minha experiência com ele!


SINGLE RESPONSIBILITY PRINCIPLE (Princípio da Responsabilidade Única)

Uma classe deve ter um, e exclusivamente um, motivo para ser alterada.

Ou seja, não devemos ter uma única classe fazendo de tudo: recebendo dados, validando requisições, aplicando regras de negócio e acessando o banco de dados...

Essa classe que “faz tudo” é a tal da “God Class”, e é um anti-pattern.

Exemplo de Código:

// ❌ Anti-pattern: God Class fazendo muita coisa
class UserAccount {
  createAccount(user: any) {
    // 1. Valida a requisição
    if (!user.email) throw new Error("Email é obrigatório");
    // 2. Aplica as regras de negócio
    user.status = "active";
    // 3. Acessa o banco de dados
    database.save(user);
    // 4. Envia o email
    emailService.send("Bem-vindo!");
  }
}

// ✅ SOLID: Responsabilidades separadas
class UserValidator {
  validate(user: any) {
    /* lógica de validação */
  }
}
class UserRepository {
  save(user: any) {
    /* lógica de banco de dados */
  }
}
class EmailSender {
  send(message: string) {
    /* lógica de email */
  }
}
Enter fullscreen mode Exit fullscreen mode

OPEN - CLOSED PRINCIPLE (Princípio Aberto-Fechado)

As partes integrantes de um código devem ser abertas para extensão e fechadas para modificações.

A ideia é evitar a dependência de verificações condicionais com if-else e switch. Quando um novo requisito chegar, você adiciona o comportamento criando algo novo, sem modificar o código antigo.

Exemplo de Código:

// ❌ Anti-pattern: Modificando código existente para cada novo tipo
class PaymentProcessor {
  process(paymentType: string, amount: number) {
    if (paymentType === "credit") {
      // processa cartão de crédito
    } else if (paymentType === "paypal") {
      // processa paypal
    } // Adicionar um novo tipo exige modificar esta classe
  }
}

// ✅ SOLID: Aberto para extensão via interfaces
interface PaymentMethod {
  process(amount: number): void;
}

class CreditCardPayment implements PaymentMethod {
  process(amount: number) {
    /* processa cartão de crédito */
  }
}

class PayPalPayment implements PaymentMethod {
  process(amount: number) {
    /* processa paypal */
  }
}

// Agora você pode adicionar novos métodos de pagamento sem tocar no código existente!
Enter fullscreen mode Exit fullscreen mode

LISKOV SUBSTITUTION PRINCIPLE (Princípio da Substituição de Liskov)

Uma classe mãe deve ser capaz de ser substituída pelas suas classes filhas sem que a aplicação quebre.

Isso na prática ajuda a organizar a ideia de herança, já que nos faz evitar estender uma classe mãe, apenas para depois remover um método já implementado ou fazer um throw new Error('Not implemented'). Fazendo com que tenhamos mais cuidado no planejamento.

Exemplo de Código:

// ❌ Anti-pattern: Quebrando a substituição
class Bird {
  fly() {
    console.log("Voando...");
  }
}

class Penguin extends Bird {
  fly() {
    throw new Error("Não implementado: Pinguins não podem voar");
  }
}

// ✅ SOLID: Melhor planejamento com abstrações
class Bird {
  eat() {
    console.log("Comendo...");
  }
}

class FlyingBird extends Bird {
  fly() {
    console.log("Voando...");
  }
}

class Penguin extends Bird {
  swim() {
    console.log("Nadando...");
  }
}
Enter fullscreen mode Exit fullscreen mode

INTERFACE SEGREGATION PRINCIPLE (Princípio da Segregação da Interface)

Nenhuma classe deve ser dependente de métodos que não precisa.

Um exemplo seria uma interface "Máquina", que força os métodos print(), scan(), e fax(). Nem toda máquina tem esses 3 métodos.

Então criamos as interfaces IPrinter, IScanner e IFax. Agora uma máquina assina apenas os contratos que realmente precisa e cumpre.

Exemplo de Código:

// ❌ Anti-pattern: Forçando métodos desnecessários
interface IMachine {
  print(): void;
  scan(): void;
  fax(): void;
}

class SimplePrinter implements IMachine {
  print() {
    console.log("Imprimindo...");
  }
  scan() {
    throw new Error("Não suportado");
  } // Não precisa disso
  fax() {
    throw new Error("Não suportado");
  } // Não precisa disso
}

// ✅ SOLID: Interfaces segregadas
interface IPrinter {
  print(): void;
}
interface IScanner {
  scan(): void;
}
interface IFax {
  fax(): void;
}

class SimplePrinter implements IPrinter {
  print() {
    console.log("Imprimindo...");
  }
}

class MultiFunctionPrinter implements IPrinter, IScanner, IFax {
  print() {
    console.log("Imprimindo...");
  }
  scan() {
    console.log("Escaneando...");
  }
  fax() {
    console.log("Enviando Fax...");
  }
}
Enter fullscreen mode Exit fullscreen mode

DEPENDENCY INVERSION PRINCIPLE (Princípio da Inversão de Dependência)

Módulos de alto nível não devem depender de detalhes de baixo nível. Ambos devem depender de abstrações (interfaces).

Ou seja, em vez da sua classe instanciar diretamente uma conexão com o banco de dados ou serviço externo, ela deve receber isso de fora. É essa inversão que permite criar mocks para testes ou trocar ferramentas no backend sem quebrar a sua regra de negócio.

Exemplo de Código:

// ❌ Anti-pattern: Módulo de alto nível depende de detalhe de baixo nível
class MySQLDatabase {
  save(data: any) {
    /* salva no MySQL */
  }
}

class UserService {
  private db = new MySQLDatabase(); // Acoplamento forte

  createUser(user: any) {
    this.db.save(user);
  }
}

// ✅ SOLID: Dependendo de abstrações injetadas de fora
interface IDatabase {
  save(data: any): void;
}

class MySQLDatabase implements IDatabase {
  save(data: any) {
    /* salva no MySQL */
  }
}

class MongoDatabase implements IDatabase {
  save(data: any) {
    /* salva no Mongo */
  }
}

class UserService {
  // Recebe a dependência de fora (Injeção de Dependência)
  constructor(private db: IDatabase) {}

  createUser(user: any) {
    this.db.save(user);
  }
}
Enter fullscreen mode Exit fullscreen mode

INDO MAIS A FUNDO

O SOLID é um conjunto de princípios complexos, e não dá pra resumir tudo em um simples amontoado de frases.

É necessário pesquisar, ler, praticar, interpretar... Então pretendo trazer por aqui uma série de novos conteúdos, destrinchando em detalhes. Pelo menos 1 tópico por princípio, com detalhes, exemplos e código.


Meus Links
Github: victor-lis-bronzo
Linkedin: victor-lis-bronzo
Portfólio: portfolio.victorlisbronzo.me
Portfólio mais legal: victorlisbronzo.me


Deixe sua reação ❤️
Qual desses 5 princípios você acha mais difícil de aplicar no seu dia a dia?

Top comments (0)