Executando verificação de segurança...
6

[SOLID] Open Close Principle

Se você já trabalhou com código por mais de cinco minutos, provavelmente já passou pela seguinte situação: você precisa adicionar uma nova funcionalidade e, de repente, percebe que vai ter que mexer em 47 arquivos diferentes, quebrar 23 testes e rezar para três santos diferentes para que tudo continue funcionando.

Bem-vindo ao mundo maravilhoso do código que não segue o Princípio Aberto/Fechado (Open/Closed Principle), a segunda letra do acrônimo SOLID!

O Que É Esse Tal de Princípio Aberto/Fechado?

O princípio é bem simples de entender:

"As entidades de software devem estar abertas para extensão, mas fechadas para modificação"

Traduzindo para o português brasileiro: seu código deve ser como um jogo bem feito: você pode instalar novas expansões para adicionar mundos e personagens, sem precisar reescrever o jogo base. Você pode adicionar novas funcionalidades (extensão) sem precisar mexer no código existente (modificação).

É como construir uma casa modular: você pode adicionar novos cômodos sem derrubar as paredes que já existem.

O Exemplo Clássico: O Pesadelo do Calculador de Áreas

Vamos começar com um exemplo que vai fazer você se identificar

O Código Que Quebra Tudo

<?php
class Rectangle {
    public $width;
    public $height;

    public function __construct(public int $width, public int $height) {
        $this->width = $width;
        $this->height = $height;
    }
}

class Circle {
    public $radius;

    public function __construct($radius) {
        $this->radius = $radius;
    }
}

class AreaCalculator {
    public function calculateArea($shape) {
        if ($shape instanceof Rectangle) {
            return $shape->width * $shape->height;
        } elseif ($shape instanceof Circle) {
            return pi() * $shape->radius * $shape->radius;
        }

        throw new InvalidArgumentException("Forma não suportada!");
    }
}

// Uso
$calculator = new AreaCalculator();
$rectangle = new Rectangle(5, 10);
$circle = new Circle(3);

echo $calculator->calculateArea($rectangle); // 50
echo $calculator->calculateArea($circle);    // ~28.27

Parece inofensivo, né? Mas agora imagine que seu chefe chega na segunda-feira e fala: "Precisamos calcular a área de triângulos também!"

Você vai ter que:

  1. Criar a classe Triangle
  2. MODIFICAR a classe AreaCalculator (violação do princípio!)
  3. Adicionar mais um elseif
  4. Torcer para não quebrar nada

E quando ele pedir hexágonos? E dodecágonos? E polígonos de 47 lados que ele viu em um sonho? Seu método vai virar uma cadeia de if/elseif maior que a constituição brasileira!

O Código Que Segue o Princípio

<?php
interface ShapeInterface {
    public function calculateArea(): float;
}

class Rectangle implements ShapeInterface {
    private $width;
    private $height;

    public function __construct(float $width, float $height) {
        $this->width = $width;
        $this->height = $height;
    }

    public function calculateArea(): float {
        return $this->width * $this->height;
    }
}

class Circle implements ShapeInterface {
    private $radius;

    public function __construct(float $radius) {
        $this->radius = $radius;
    }

    public function calculateArea(): float {
        return pi() * $this->radius * $this->radius;
    }
}

class Triangle implements ShapeInterface {
    private $base;
    private $height;

    public function __construct(float $base, float $height) {
        $this->base = $base;
        $this->height = $height;
    }

    public function calculateArea(): float {
        return ($this->base * $this->height) / 2;
    }
}

class AreaCalculator {
    public function calculateArea(ShapeInterface $shape): float {
        return $shape->calculateArea();
    }
}

// Uso
$calculator = new AreaCalculator();
$shapes = [
    new Rectangle(5, 10),
    new Circle(3),
    new Triangle(4, 6)
];

foreach ($shapes as $shape) {
    echo "Área: " . $calculator->calculateArea($shape) . "\n";
}

Viu a diferença? Agora quando seu chefe pedir para calcular a área de um dodecágono, você só precisa criar a classe Dodecagon implementando ShapeInterface. A classe AreaCalculator continua intacta, feliz e funcionando perfeitamente!

Exemplo Real: Sistema de Notificações

Vamos para um exemplo mais próximo da vida real. Imagine um sistema de notificações.

A Abordagem "Vou Quebrar Tudo" ❌

<?php
class NotificationService {
    public function send($message, $type, $recipient) {
        switch ($type) {
            case 'email':
                $this->sendEmail($message, $recipient);
                break;
            case 'sms':
                $this->sendSMS($message, $recipient);
                break;
            case 'push':
                $this->sendPushNotification($message, $recipient);
                break;
            default:
                throw new InvalidArgumentException("Tipo de notificação inválido!");
        }
    }

    private function sendEmail($message, $recipient) {
        // Lógica para enviar email
        echo "Email enviado para: $recipient - $message\n";
    }

    private function sendSMS($message, $recipient) {
        // Lógica para enviar SMS
        echo "SMS enviado para: $recipient - $message\n";
    }

    private function sendPushNotification($message, $recipient) {
        // Lógica para enviar push notification
        echo "Push notification enviado para: $recipient - $message\n";
    }
}

Agora seu produto manager aparece e diz: "Precisamos integrar com WhatsApp, Telegram, Slack e Discord!"

A Abordagem "Minha Sanidade Mental Agradece" ✅

<?php
interface NotificationInterface {
    public function send(string $message, string $recipient): bool;
}

class EmailNotification implements NotificationInterface {
    public function send(string $message, string $recipient): bool {
        // Lógica específica para email
        echo "📧 Email enviado para: $recipient - $message\n";
        return true;
    }
}

class SMSNotification implements NotificationInterface {
    public function send(string $message, string $recipient): bool {
        // Lógica específica para SMS
        echo "📱 SMS enviado para: $recipient - $message\n";
        return true;
    }
}

class WhatsAppNotification implements NotificationInterface {
    public function send(string $message, string $recipient): bool {
        // Lógica específica para WhatsApp
        echo "💬 WhatsApp enviado para: $recipient - $message\n";
        return true;
    }
}

class SlackNotification implements NotificationInterface {
    public function send(string $message, string $recipient): bool {
        // Lógica específica para Slack
        echo "💼 Slack enviado para: $recipient - $message\n";
        return true;
    }
}

class NotificationService {
    private $notifications = [];

    public function addNotificationMethod(NotificationInterface $notification) {
        $this->notifications[] = $notification;
    }

    public function sendToAll(string $message, string $recipient) {
        foreach ($this->notifications as $notification) {
            $notification->send($message, $recipient);
        }
    }

    public function send(NotificationInterface $notification, string $message, string $recipient) {
        return $notification->send($message, $recipient);
    }
}

// Uso
$notificationService = new NotificationService();

// Configurando os métodos de notificação
$notificationService->addNotificationMethod(new EmailNotification());
$notificationService->addNotificationMethod(new SMSNotification());
$notificationService->addNotificationMethod(new WhatsAppNotification());

// Enviando notificação
$notificationService->sendToAll("Seu pedido foi confirmado!", "[email protected]");

// Ou enviando por um canal específico
$notificationService->send(new SlackNotification(), "Deploy realizado com sucesso!", "#dev-team");

Agora quando precisar adicionar Discord, Telegram, pombo-correio ou sinais de fumaça, você só cria uma nova classe. O NotificationService nem pisca!

Por Que Isso É Tão Importante?

1. Manutenibilidade:

Você não quebra código existente quando adiciona funcionalidades novas. É como adicionar um novo sabor de pizza no cardápio sem precisar reformar a cozinha inteira.

2. Testabilidade:

Cada classe tem sua responsabilidade específica. Testar fica muito mais fácil.

3. Reutilização:

Suas classes ficam modulares. Pode usar EmailNotification em qualquer lugar sem carregar toda a bagagem do sistema.

4. Escalabilidade:

Adicionar novas funcionalidades vira uma operação segura. Seu coração não vai mais acelerar a cada deploy.

Dicas Práticas Para Não Pirar

1. Use Interfaces e Classes Abstratas

Elas são seus melhores amigos para definir contratos que suas classes devem seguir.

2. Pense em Strategies (O design pattern)

Quando você vê um switch/case ou uma cadeia de if/elseif gigante, geralmente é sinal de que está violando o princípio.

3. Dependency Injection é amigo

Injete dependências ao invés de criar instâncias dentro das classes. Isso facilita a extensão.

4. Não Exagere na Abstração

Não tente prever todos os cenários futuros. Refatore quando a necessidade real aparecer. YAGNI (You Ain't Gonna Need It) é real!

Conclusão: Seu Futuro Eu Vai Te Agradecer

O Princípio Aberto/Fechado pode parecer trabalhoso no início, mas é um investimento na sua sanidade mental futura. É a diferença entre ser aquele desenvolvedor que adiciona funcionalidades com confiança e aquele que precisa de três cafés e uma reza antes de cada commit.

Lembre-se: código bem estruturado é como um bom relacionamento - você pode crescer juntos sem precisar mudar completamente quem vocês são.

E quando seu chefe vier com mais uma "pequena mudança", você vai poder sorrir e dizer: "Sem problemas, só vou criar uma nova classe!"

Agora pare de ler e vá refatorar aquele switch gigante que você sabe que está lá te julgando! 😄

Carregando publicação patrocinada...
4

Novamente o exemplo não consegue mostrar claramente qual é o problema com ele.

Qual é exatamente o problema de violar esse princípio? Só porque você teve que modificar a função calculateArea ? Por que é tão ruim assim modificar? Como adicionar mais um if iria quebrar alguma coisa?

Nos dois exemplos "ruins", não há os problema que a suposta solução resolve.

  1. Manutenibilidade

Substituir if/else/switch por polimorfismo é trocar seis por meia dúzia. E se o seu chefe vier e pedir pra adicionar uma função calculatePerimeter ? Você vai ter que mexer na interface e em TODAS as classes. Ou seja, não existe um melhor ou pior, vai depender do que você prefere.

  1. Testabilidade

Por que exatamente o código com if/else/switch ficaria mais difícil de testar? Não consigo ver como a versão "ruim" ficaria difícil testar.

  1. Reutilização

Por que exatamente o código com if/else/switch não conseguiria usar em outros lugares? Dá pra usar a função calculateArea em qualquer lugar. Qual bagagem que levaria?

  1. Escalabilidade

Por que exatamente o código com if/else/switch não seria uma "operação segura" ?

No post inteiro só vejo afirmações fortes mas sem uma argumentação clara dos reais problemas.

Também vejo comparações:

"Seu método vai virar uma cadeia de if/elseif maior que a constituição brasileira!"

que não fazem nenhum sentido. Qual o problema de uma cadeia de ifs?

Conclusão

De novo, o código "problemático" na verdade não tem nenhum problema.

Assim como o primeiro post, esse foi uma tentativa de mostrar como o princípio é útil, no entanto não consegue fazer isso. Na minha visão parece apenas uma visão ideológica de princípios abstratos que não servem pra nada mas as pessoas insistem em a defender.

-3
0

Também concordo. Uma coisa que não entendo é o seu comentário levar downvote mas os dois posts dele que são de péssima qualidade se mantém com pontuação positiva. Parece uma certa 'censura' com coisas muito negativas (mesmo que seja apenas uma opinião) e não contra conteúdo ruim, que deveria ser o foco.

Aposto que se eu fizesse um post falando mal de SOLID eu iria ter uma chuva de downvote e um monte de comentário falando como eu estou errado. Mas quando alguém posta algo positivo, mesmo que de baixa qualidade, ainda recebe upvote e nenhum comentário criticando.

Também aposto que se eu não tivesse feito meu comentário bem perto de quando foi postado, o post teria vários upvotes igual o primeiro. (Edit: Parece que o post mesmo assim conseguiu uns 3 upvotes, não faço ideia de como isso é possível depois do meu comentário. Será que o autor tem contas extras?)

Realmente, a qualidade dos posts e pessoas que utilizam o tabnews é bem baixa no geral.

1

Olá! Eu, particularmente, gostei do post. Achei relevante e bem escrito. Então dei um upvote. Espero que não fique chateado comigo. 😁

Não acho que haja censura por aqui. No geral, é uma comunidade acolhedora e que respeita opiniões diversas.

Quanto às pessoas que recebem downvotes por criticar, percebo que normalmente não é pela crítica em si, mas pelo tom. Não estou dizendo que foi o seu caso, pois considero que suas colocações se encaixariam nunca categoria "agressiva respeitosa". Mas sempre existem formas melhores de comunicar puxando mais para o diálogo e debate saudável.

Sem te conhecer, diria que ficaste muito incomodado com o texto do autor e canalizou as emoções em suas falas. Como se quisesse iniciar uma briga ou disputa. Posso estar bem enganado, mas foi o sentimento que suas palavras me transmitiram.

1

Achei relevante e bem escrito

Porque você achou relevante e bem escrito?

Esse é outro problema do post e também do seu comentário (e também de quase tudo na internet), são feitas apenas afirmações mas nunca dão uma razão sólida para tais afirmações.

Ao meu ver o post não é relevante porque não apresenta nenhum motivo do código "ruim" ser ruim. Também não é bem escrito porque não utiliza um exemplo real. Exemplos simples que não tem uma formulação clara do problema que resolve não se assemelha em nada código que você escreve em produção. É tudo uma visão ideológica de credulidade porque outros falaram que SOLID é bom. Não tem nenhum argumento lógico no post que persuade o leitor de que o princípio é importante ou que resulta em código melhor. E tudo isso é verdade porque até agora ninguém e principalmente o autor não fez nenhum contra argumento aos meus comentários. Por isso que eu não entendo as pessoas que deram upvote no post ou downvote no meu comentário. Ninguém tenta dialogar ou debater, simplesmente deixa o voto e vai embora.

Não acho que haja censura por aqui. No geral, é uma comunidade acolhedora e que respeita opiniões diversas.

Faz um "post falando mal de SOLID" que você vai entender. Daí você vai ver o que é briga e agressividade.

iniciar uma briga ou disputa

Iniciar uma briga com o quê? Por apontar as falhas do post? Por querer conteúdo de qualidade no tabnews? Se eu quisesse iniciar um briga eu xingava a mãe dele.

suas colocações se encaixariam nunca categoria "agressiva respeitosa"

Agressivo? Sério? Onde? Agora apontar as falhas do post por fazer perguntas é considerado agressivo? Essas perguntas também são agressivas?

Provavelmente porque perguntas fazem a pessoa pensar, e isso gera desconforto nas pessoas do tabnews.

1

Não tenho contas extras amigo! Eu considero SOLID bons princípios mesmo tendo outras opções menos robustas, meu intuito aqui é apenas espalhar informações. Mas SOLID não é lei! Você não precisa usar caso não veja utilidade

1

outras opções menos robustas

De novo afirmações sem nenhuma base. Em nenhum momento você demonstrou que as outras soluções são menos robustas. Como que SOLID iria resolver um problema que nem existe?

apenas espalhar informações

O problema é que são informações ruins.

SOLID não é lei!

Eu sei que não é lei, mas posts como esse dão a entender que é tão importante que é praticamente uma lei. Por exemplo:

Agora pare de ler e vá refatorar aquele switch gigante que você sabe que está lá te julgando! 😄

Depois de uma conclusão dessa, como a pessoa não vai pensar que isso é quase uma lei?

Você não precisa usar caso não veja utilidade

Eu realmente não utilizo e não entendo como pessoas acham que ajuda em algo mesmo eu mostrando que não ajuda em nada. Também não entendo como você ainda considera bons princípios sendo que você não deu nenhum contra-argumento nos meus comentários.