O artigo aborda quatro dos cinco padrões GoF criacionais (Builder, Abstract Factory, Factory Method, Prototype e Singleton). Neste artigo você aprenderá como implementar os padrões Abstract Factory, Factory Method, Prototype e Singleton.
Para que serve
Todo projeto precisa criar instâncias de classes, mas se isto não for feito de forma controlada, o projeto tende a tornar-se complexo e rígido, dificultando a manutenção do mesmo. Os padrões criacionais são técnicas que ajudam controlar como as instâncias de classes são criadas.
Em que situação o tema é útil
Todo projeto de software orientado a objetos precisa criar instâncias de suas classes. Os padrões criacionais são técnicas que tornam o sistema menos rígido permitindo que as instâncias sejam criadas sem que instruções como TMinhaClasse.Create sejam espalhas descontroladamente.
Padrões criacionais
Neste artigo sobre os padrões de criação, serão abordados quatro dos cinco padrões GoF Criacionais. Você aprenderá como construir verdadeiras fábricas de objetos, capazes de controlar a criação de famílias inteiras de classes usando os padrões Abstract Factory, Factory Method, Prototype e Singleton.
Os padrões GoF documentam cinco padrões criacionais: Builder, Abstract Factory, Factory Method, Prototype e Singleton. Apesar de estes padrões possuírem implementações diferentes entre si, resumidamente todos têm o objetivo de controlar como as classes do projeto serão criadas, evitando o uso descontrolado de instruções como “TMinhaClasse.Create”. A diferença entre estes padrões está nas consequências que cada um deles carrega consigo – consequências boas e/ou ruins.
O problema em espalhar o uso do construtor das classes sem controle algum é que o projeto fica preso às implementações quando deveria estar preso às interfaces. Consequentemente um projeto assim torna-se inflexível, o que aumenta o tempo gasto com manutenções. Imagine um projeto com uma interface ICarro. Esta interface possui diversas implementações diferentes, como por exemplo, TMustang, TFerrari, TAudiR8 e assim por diante. Se a criação/fabricação destas classes não for separada e controlada em um único local, em pouco tempo o projeto estará confuso e cheio desvios (instruções “if else”, por exemplo), necessários para decidir qual das implementações de ICarro deve ser usada.
Neste cenário adicionar um novo tipo de carro, será uma tarefa cansativa e talvez desastrosa, pois o programador terá que procurar e alterar todos os locais onde existe uma tomada de decisão para descobrir qual tipo de carro deve ser criado. Este tipo de ação normalmente gera erros, exatamente por ser uma tarefa cansativa. A situação citada neste exemplo pode ser facilmente contornada se a forma comum de se criar um objeto, usando “TMinhaClasse.Create” for evitada.
O problema
Dando continuidade ao exemplo dos carros, imagine um projeto para jogo de corridas. Neste projeto existem diversos tipos de carros: TMustang, TFerrari, TAudiR8 e assim por diante. Essas classes são usadas em diversos pontos do projeto. Para poder criar um tipo de carro, é sempre necessário avaliar algum parâmetro para decidir qual o carro escolhido pelo usuário. Sendo assim, sempre que um carro precisa ser criado, uma instrução parecida com a Listagem 1 deve ser escrita.
O código é extremamente simples. Talvez seja devido a esta simplicidade que este tipo de codificação não receba a devida atenção. A verdade é que apesar de simples, instruções como essa podem causar verdadeiros desastres ao projeto. Se um carro precisar ser criado em vários pontos diferentes do projeto, logo instruções como a da listagem em questão estarão espalhadas por todo o projeto e quando um novo tipo de carro for adicionado, todos estes pontos deverão ser alterados.
Esta forma de programar – espalhando a criação dos carros em vários pontos – desrespeita dois princípios importantes: Programe Para Interface e o Princípio de Responsabilidade Única. A seguir veja um pouco mais sobre estes dois princípios.
Listagem 1. Criando um tipo de carro
1 Var
2 Carro: ICarro;
3 Begin
4 if GetTipo = 1 then
5 Carro := TMustang.Create
6 else if GetTipo = 2 then
7 Carro := TFerrari.Create
8 else if GetTipo = 3 then
9 Carro := TAudiR8.Create
10 Else
11 raise Exception.Create('Tipo de carro desconhecido');
12 //Restante do código
13 end;
Princípios de orientação a objetos
Programar orientado a objeto é tão simples quanto programar de forma estruturada, ainda mais quando se programa em Object Pascal usando Delphi como IDE. Para criar um projeto orientado a objetos, a única coisa que o programador precisa fazer é criar classes: ele não precisa implementar padrões ou seguir princípios. Usando o Delphi, fica ainda mais fácil, pois o programador estará criando um projeto orientado a objetos mesmo que não saiba o que isto significa.
Apesar de ser simples se o programador não seguir alguns princípios e padrões quando criar um projeto orientado a objetos, ele provavelmente terá muitos problemas, devido à falta de organização estrutural do código. Isto significa que a POO só é vantajosa quando usada de maneira apropriada: usando os padrões e princípios comprovados pela comunidade de desenvolvedores.
Estes padrões e princípios são técnicas que ajudam o desenvolvedor criar um projeto que aproveite o máximo das vantagens que a POO oferece, como: flexibilidade, simplicidade e baixo custo de manutenção. De fato, os padrões de projetos são técnicas que usam os princípios da OO para solucionar problemas de projetos. Por serem guiadas pelos princípios as soluções propostas pelos padrões de projetos acabam sendo flexíveis e reutilizáveis.
Programe para interfaces