Artigo no estilo Mentoring
Mentoring:Softwares reais evoluem. Eles são constantemente modificados e adaptados, novos recursos são implementados, bugs são corrigidos, outros criados e aparecem do nada, novas exigências são feitas, a lei muda, o cliente pede, o código vai se tornando mais complexo e se afasta do seu projeto original, diminuindo sua qualidade. Está mais do que óbvio que a maior parte do custo total do desenvolvimento do software é dedicada à sua manutenção, em alguns casos, esse valor pode chegar a 80% do seu custo final. Se uma aplicação precisa ser modificada em um determinado momento, seja para manutenção corretiva ou evolutiva para atender novas funcionalidades, este processo precisa ser feito de forma disciplinada, correta, usando padrões, já que o custo da manutenção representa um porcentual elevado comparado ao custo total do sistema, como citado.

Com isso, o uso de padrões de projeto, ou Design Patterns, torna um software mais fácil de ser mantido em longo prazo, reduzindo custos ou otimizando o retorno do investimento, apesar de exigir um maior esforço em um primeiro momento, durante o design do projeto a ser realizado. Como muitas empresas não realizam este projeto da forma correta na concepção do software, seja pela falta de tempo devido a prazos reduzidos, desconhecimento da equipe sobre uso de boas práticas de programação orientada a abjetos, arquitetura ou uso de padrões, execução de múltiplos projetos simultaneamente, ocorre o chamado débito de projeto. São softwares que atendem aos seus requisitos em um estágio inicial, mas são extremamente rígidos em sua estrutura, difíceis de manter e evoluir, repleto de “bad smells”, principalmente se essa evolução for feita por outro desenvolvedor que não o próprio criador. Um bad smell, ou simplesmente mau cheiro, é uma indicação superficial que leva a um problema profundo no código de um sistema. O termo foi criado por Kent Beck e utilizado no livro Refactoring: Improving the Design of Existing Programs de Martin Fowler [2].

Este artigo apresenta os fundamentos sobre Padrões de Projeto e implementações de exemplos práticos reais em Delphi. Neste artigo, serão tratados os principais padrões criacionais do GoF [1], destinados a resolver problemas com criação de objetos concretos, que comprometem a reutilização de módulos do sistema e aumentam o acoplamento. Os padrões apresentados neste artigo são o Abstract Factory, Factory Method, Singleton e Builder.


Em que situação o tema é útil
Padrões de projeto são extremamente úteis para criar softwares mais fáceis de serem mantidos, evoluídos, promovendo reutilização de código, uso de boas práticas de programação orientada a objetos, redução de custos com manutenção, já que permitem que alterações em um determinado sistema de software não quebrem outras funcionalidades, reduzindo a dependência entre classes, através do uso exaustivo de abstrações. Dessa forma, são úteis para serem aplicados em projetos de média e larga escala.

O conhecimento sobre padrões de projeto só faz sentido para aqueles que desenvolvem softwares de forma orientada a objetos. Não faz sentido tentar aplicar padrões de projeto se você programa centenas de linhas de código em eventos de um controle de user interface em um formulário Delphi, sem separar responsabilidades. Não faz sentido tentar usar padrões se você usa event-driven programming (programação dirigida ou orientada a eventos).

Se o software feito em Delphi da sua empresa precisa passar por uma evolução, modernização, migração, porém está mal escrito, repleto de bad smells, onde dezenas de programadores já colocaram a mão, e o custo para mantê-lo está saindo caro, talvez a melhor atividade antes de aplicar padrões de projeto seja a refatoração. Aplicar refatorações primitivas, como extrair métodos, classes, superclasses, pode tornar o seu software mais modular, simples de manter e evoluir. A partir daí, o uso de padrões de projeto pode ajudar a melhorar os atributos de qualidade do seu software.

Uma outra atividade, muito interessante, é aplicar padrões de projeto no contexto de refatorações (BOX 1).

BOX 1. Refatoração

A refatoração (do inglês Refactoring) é o processo de modificação do sistema de software sem alterar seu comportamento externo observável, como funcionalidades, tendo como alguns dos seus benefícios melhorar o entendimento do código, remover duplicidades, simplificar métodos, facilitando assim a manutenção, correção de bugs, adição de novas funcionalidades, aumentando a qualidade do produto [3].

Ou seja, nunca se aplica um padrão de forma antecipada, pois não sabe se realmente ele será necessário, causando uma complexidade desnecessária, o que chamamos de excesso de engenharia. Programadores não são videntes. Em contrapartida, nunca aplicar um padrão causa uma escassez de engenharia muito comum em aplicações que seguem o modelo RAD de desenvolvimento, como Delphi (existem programadores que usam Delphi há mais de 15 anos e nunca criaram uma classe). O software já tem 10 anos, está na versão 5, ninguém mais entende como ele funciona, o que foi feito, as pessoas perdem a credibilidade, a motivação, os erros são muitos, uma nova funcionalidade que deveria ser implementada em minutos leva horas ou dias, a correção de um bug gera novos bugs, a rotatividade de membros na equipe de desenvolvimento da empresa está alta, e isso está cada vez pior. Tão ruim que o caos já está generalizado e a empresa decide em algum momento reescrever o software do zero, pois não há mais conserto. Nesse sentido, a prática de aplicar padrões de projeto em código existente, no contexto de uma refatoração, se torna uma atividade bastante atraente.

Padrões de projeto se baseiam fortemente em princípios básicos da programação orientada a objetos, mais especificamente, na herança, abstração, encapsulamento e polimorfismo. Aqui é importante distinguir entre herança de classe da herança de abstrações. As heranças que veremos nos padrões são todas de abstrações, de interfaces, sem implementação. É uma herança de apenas dois níveis, já que criar cadeias muito grandes de hierarquia causa complexidade e dificulta a manutenção. Nesse ponto, uma herança de “caixa preta” se torna mais atraente, como faz o padrão Decorator. Se não há o domínio estes conceitos, principalmente a implementação de classes abstratas por polimorfismo, é recomendado fortemente que faça uma pesquisa e se domine estes fundamentos antes de entrar de cabeça no mundo dos padrões.

Os padrões discutidos neste artigo são apresentados no famoso livro “Design Patterns: Elements of Reusable Object-Oriented Software”, escrito em 1995 por Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides [1]. Eles ficaram conhecidos como os padrões GoF (Ganf of Four, ou “gangue dos quatro”), em alusão aos seus autores. Ao todo são 23 padrões de projeto, divididos em três categorias: criacionais, estruturais e comportamentais. Este artigo aborda os principais padrões do grupo criacionais.

Padrões de projeto são largamente utilizados no próprio Delphi. Apesar do Delphi ter sido desenvolvido antes do lançamento do livro (o Delphi foi criado a partir do Borland Pascal for Windows na década de 90, que veio do Turbo Pacal da década de 80), pode-se notar muitos padrões de projeto na própria VCL do Delphi e no IDE. Por exemplo, a VCL inteira não deixa de ser uma grande “fachada” para esconder a complexidade e total deselegância da implementação procedural da API do Windows baseada em ponteiros sem tipo e sem classes. É a mais clara implementação que se pode ter do padrão Facade, assim como o próprio .NET Framework também é. Isso porque esses frameworks encapsulam um subsistema inteiro tornando-o mais fácil de utilizar. Quando olhamos para a parte visual da VCL, como os controles, formulários, botões, panels, e o modelo de componente (TComponent), vemos a implementação clara do padrão Composite (composição). De fato, podemos tratar um formulário como uma composição de outros componentes, enquanto o próprio TForm também é tratado como um TComponent (pois em algum momento herda dele). Um DataSource, ao apontar para um DataSet, usa abstração para esconder um tipo concreto e resolve uma chamada Post de forma polimórfica, a fim de usar uma das n diferentes formas de persistir dados em um banco de dados a partir de dados vindos da tela. Ele esconde uma estratégia para persistir os dados, de fato, você pode trocar um TTable por um TClientDataSet configurados da forma correta e os dados serão gravados no mesmo local. É uma espécie de padrão Strategy. Existem objetos na VCL que podem ser utilizadas uma única vez, como Application, por exemplo, o que poderia caracterizar um Singleton. Quando chamamos um método Post de qualquer implementação de um TDataSet, inúmeros outros métodos polimórficos são chamados em TDataSet mas que só são conhecidos em classes descendentes. Eles só estão em TDataSet para serem chamados de acordo com um determinada ordem, um típico Template Method. E por aí vai, existem dezenas de padrões implementados na VCL (e FireMonkey), comprovando que mesmo na década de 90 a equipe de desenvolvimento da então Borland tinha fortes conhecimentos em orientação a objetos. Essas soluções, não só as utilizadas pela equipe de desenvolvimento da Borland, se tornaram tão claras, que foram documentadas para que pudessem ser reutilizadas em outros projetos, como receitas de bolo prontas. Nascia o livro Padrões de Projeto do GoF.

...
Quer ler esse conteúdo completo? Tenha acesso completo