Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Categoria de Programação
Postado em 14 abril 2022
Atualizado em 03 junho 2023
Palavras-chave: arquitetura,open,closed,principle,principio,aberto,fechado,programação,programming
Visualizações: 1806
Quanto mais dependências uma classe possui, mais estável ela se torna. Uma modificação em uma classe estável poderia gerar um efeito cascata em suas dependências transitivas, causando comportamentos inesperados em classes dependentes sem ninguém perceber. Por isso é importante estar ciente do ciclo de dependências que uma classe possui. Porém, nem sempre podemos estar checando as dependências entre as classes, por isso queremos estabelecer regras de desenvolvimento para evitar que isso aconteça.
O princípio aberto fechado pode ser uma solução para o problema de estabilidade, conservando a estrutura de uma classe através de abstrações.
O princípio aberto fechado é o segundo princípio abordado nos princípios SOLID. Seu criador é Bertrand Meyer e mais tarde foi adotado por Robert Martin, tendo mais destaque após a divulgação da arquitetura limpa.
Esse princípio diz que devemos ser capazes de modificar o comportamento de uma classe sem tocar no código existente. Em outras palavras, devemos ser capazes de estender abstrações para mudar o comportamento de uma classe.
Por exemplo, imagine que temos um software capaz de criar documentos em formato PDF. Entretanto, por exigência do cliente, foi solicitado uma modificação no formato dos documentos para Excel. Nesse caso, não há necessidade de modificar o código existente para implementar a criação de documentos Excel. Invés disso, apenas adiciona-se mais código e substitui-se o argumento atual.
Aplica-se o princípio aberto fechado criando dependências em abstrações. As abstrações podem ser classes abstratas ou interfaces. Uma abstração é um contrato contendo uma estrutura de métodos que devem ser implementados em uma classe que depende de uma abstração.
public interface DocumentPrinterInterface {
void print(String fileName);
}
O exemplo acima é uma simples abstração. Mesmo não possuindo conteúdo, a interface acima pode ser definida como parâmetro.
public class Customer {
DocumentPrinterInterface documentPrinter;
public Customer(DocumentPrinterInterface documentPrinter) {
this.documentPrinter = documentPrinter;
}
public void printCustomerStats() {
// ...
documentPrinter.print("customer_xx_stats");
}
}
Veja como a interface pode ser definida como parâmetro mesmo sem ter conteúdo dentro dela. Quando usamos uma interface como parâmetro, isso quer dizer que já definimos o contrato que deve ser obedecido pelas classes. Isso pode ser bastante vantajoso quando queremos definir regras de implementação.
Porém, uma interface não pode ser instanciada. Para uma interface ser instanciada, ela deve ser implementada por uma classe contendo detalhes.
public class PrintPdf implements DocumentPrinterInterface {
ViewModel data;
PDFLibrary pdfLibrary;
void print(String fileName) {
pdfLibrary.print(fileName);
}
}
A classe acima injeta uma biblioteca de PDF e obedece ao contrato da interface que ela implementa. Uma vez que uma classe implementa uma interface, ela é obrigada a implementar os métodos da interface.
Agora que a interface foi implementada, ela pode ser instanciada e passada como parâmetro para o construtor da classe “Customer”.
PrintPdf printPdf = new PrintPdf();
Customer customer = new Customer(printPdf);
customer.printCustomerStats();
Observe que “Customer” só aceita a classe “PrintPdf” como parâmetro, porque ela implementa a interface “DocumentPrinterInterface”. Isso significa que podemos passar qualquer classe como parâmetro se ela implementar essa interface.
// PrintPdf printPdf = new PrintPdf();
PrintExcel printExcel = new PrintExcel();
Customer customer = new Customer(printExcel);
customer.printCustomerStats();
Veja que conseguimos mudar o comportamento da classe “Customer” implementando a abstração que ela depende. Agora, ao invés de gerar documentos em PDF, a classe “Customer” irá gerar documentos em formato Excel.
Uma classe que possui muitas dependências se torna estável pelo fato de poder afetar o funcionamento das suas dependências. Quando alteramos alguma coisa dentro de uma classe estável, devemos estar checando as suas dependências para ter certeza que ainda continuam funcionando. Esse teste inclui as dependências transitivas dessa classe estável.
Muitas dependências podem acabar exigindo muito mais tempo para o teste do que para a modificação. Se o processo de teste for automatizado, pode-se economizar muito mais tempo. Porém, dependendo da alteração do código, erros inesperados podem passar despercebidos pelo teste automático. Logo, a melhor solução para evitar erros inesperados é fazer com que o código esteja aberto para estensão e fechado para modificação. Em outras palavras, mudamos o comportamento de uma funcionalidade apenas adicionando mais código.
Uma grande vantagem do princípio aberto fechado é que podemos visualizar novas funcionalidades facilmente no nível arquitetural. Usando o código acima como base, veja a arquitetura abaixo como as dependências entre as classes funcionam.
Veja como todas as classes apontam para a interface “DocumentPrinterInterface”. As flechas representam as dependências. Na imagem acima, todas as classes dependem da interface. Logo, a interface é estável. Mas pelo fato da interface ser uma abstração, dificilmente modificamos ela. Isso é maravilhoso, pois não queremos mudar uma classe estável.
Robert Martin, autor dos princípio SOLID, cita no livro dele a regra de dependências. A regra de dependências diz que os detalhes devem depender de abstrações. A nossa arquitetura acima, está de acordo com a regra de dependências, pois as classes (detalhes) dependem da interface (abstração).
Quando obedecemos a regra de dependências, adquirimos uma grande vantagem: conseguimos aplicar o princípio aberto fechado. Perceba que se quiséssemos adicionar um novo formato de documento, poderíamos apenas adicionar mais código. Isso é possível apenas implementando a interface “DocumentPrinterInterface”. Assim, o nosso código se tornará aberto para estensões e fechado para modificações.
O princípio aberto fechado possibilita alterar o comportamento de uma classe estável sem modificar o código existente. Isso é possível fazendo com que os detalhes dependam das abstrações. Para que o OCP sejá aplicável, é necessário aplicar a regra mais básica da arquitetura limpa: a regra de dependências.
Projetos práticos
Implementando um programa que encontra a menor distância entre dois pontos dentro de um labirinto usando o algoritmo A* (a-estrela).
Projeto de comunicação entre dois dispositivos ESP8266 e Raspberrypi4. Laravel irá funcionar como servidor e receptor de dados de temperatura e umidade coletados com o DHT11.
Jogo simples de guerra espacial desenvolvido em javascript. Esse jogo usa cálculos de física para simular efeitos de atrito e inércia.
Detectando objetos que entram dentro do campo de visão do personagem. Útil para servir de "gatilho" para eventos em um jogo.
Programando o clássico jogo da serpente usando o framework p5.js. Tutorial indicado para iniciantes da programação que querem aprender os conceitos básico da área criando jogos.
Um computador conectado à internet está exposto a diversos perigos. O spyware é um deles e é esse malware responsável por roubar contas de redes sociais.
Estudar o comportamento das pessoas pode auxiliar um administrador a criar um sistema de fiscalização mais eficiente, evitando fraudes que prejudicam a imagem da empresa
Esse princípio diz que uma classe derivada deve ser substituível pela sua classe base sem apresentar comportamentos inesperados.
Bastante parecido com um farol que serve como sinalização para navios. Beacon tem como função enviar informações e orientações para dispositivos próximos.
Princípio que diz que um módulo só deve mudar por um único motivo. Esse motivo pode ser o conteúdo de um módulo ou os atores que dependem dele.
A programação orientada a objetos possui um contexto bastante semelhante com a vida real, facilitando a sua implementação e interpretação.