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: 1807
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
Convertendo imagens para ascii art usando o valor da intensidade das cores cinzentas.
Projeto de criação de um sistema de busca usando o framework Symfony e Elasticsearch. A integração com Kibana também é feito de modo remoto com um raspberrypi.
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.
Usando lógicas matemáticas como trigonometria para criar e calcular o esqueleto de um jogo de tiro 2D em javascript
Usando JavaFX e arquitetura limpa para criar um aplicativo de caixa eletrônico extremamente simples.
Expressões como gastar tempo e investir tempo andam lado a lado. Quando algo investido não tem o retorno esperado isso se torna uma perde de tempo...
A engenharia social é a forma mais fácil de roubar os dados da vítima, principalmente aqueles que não estão antenados..
Esse princípio diz que uma classe derivada deve ser substituível pela sua classe base sem apresentar comportamentos inesperados.
Pilha e fila são tipos de estrutura de dados que contribuem para um gerenciamento de dados mais inteligente e eficaz na programação
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.
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.