menu

Introdução

Como programadores de software, temos que tentar encontrar sempre a melhor forma de estruturar o nosso código. Existem muitos problemas que se vão tornando familiares para muitos programadores ao longo do tempo. É aqui que os padrões de design entram em ação. Padrões de design foram desenvolvidos como métodos para resolver problemas comuns. Então, decidimos aprender um pouco mais sobre este tópico e aplicamos as nossas aprendizagens numa situação de vida real.
Neste artigo, falaremos sobre alguns dos padrões de design mais utilizados e como é que eles podem ser aplicados no dia a dia de um programador.

State-of-the-art

O conceito dos padrões de design apareceu pela primeira vez em 1977, num livro chamado “A Pattern Language: Towns, Buildings, Construction“, escrito por Christopher Alexander. Na época, tratava-se da construção e desenho urbano com base nos padrões dos mais belos edifícios/construções do mundo.  Anos depois, em 1994, um grupo de cientistas da computação conhecido como Gang of Four pegou nessa ideia e aplicou-a ao desenvolvimento de software. Isso foi documentado num livro “Design Patterns: Elements of Reusable Object-Oriented Software“. Neste livro, um total de 23 padrões de design são descritos, e muitos deles ainda são aplicáveis pela maioria dos programadores em diversas situações. Assim, a ideia principal é: “Padrões de design são soluções elegantes para problemas repetitivos de design de software”.

Os padrões são divididos em 3 grupos principais:

  • Criacional — os padrões de design criacionais tratam, como o nome indica, de criar objetos. Eles fornecem mecanismos que aumentam a flexibilidade e a reutilização do código.
  • Comportamental — padrões de design comportamentais dizem respeito à comunicação/interação entre objetos para torná-los mais eficientes.
  • Estruturais — padrões de design estruturais focam-se na relação entre objetos. Eles explicam como devemos combinar objetos e classes em estruturas maiores, mantendo essas estruturas flexíveis e eficientes.

Desenvolvimento

Para o nosso estudo, selecionamos 4 dos padrões de design mais utilizados: Singleton, Builder, Factory Method e Observer. Explicaremos em detalhes como é que eles funcionam.

Padrão Singleton 

Este padrão criacional é responsável por criar um objeto e por garantir que apenas uma instância desse mesmo objeto é criada. Ele também fornece um ponto de acesso global para essa instância. Por exemplo, um problema comum onde este padrão pode ser aplicado é caso uma organização deseje adicionar uma nova funcionalidade de impressão às suas aplicações. Isso requer uma única instância do objeto, ou seja, a impressora, e um ponto de acesso global para vários aplicativos.

 

Em termos de código, implementar uma biblioteca num sistema de impressão é bastante simples. A classe singleton geralmente tem 3 partes principais:

  • Um membro estático: contém a instância da classe singleton;
  • Construtor privado: ele impede qualquer outra pessoa de instanciar a classe;
  • Método público estático: fornece o ponto global de acesso ao objeto.

A próxima imagem ilustra uma possível implementação de uma solução da impressora com um singleton em C#.

 

Padrão Observador

O observador é um padrão de design comportamental que permite definir um mecanismo de subscrição para notificar vários objetos sobre qualquer evento que ocorra com o objeto que estão a observar. Imagina que tens dois tipos de objetos: o cliente e uma loja. O cliente está interessado num produto de uma marca X (por exemplo, um novo modelo de iPhone) que ficará disponível em loja brevemente. O cliente tem a opção de visitar a loja todos os dias para verificar a disponibilidade do produto, mas enquanto o produto estiver a caminho, essas visitas seriam inúteis.
Por outro lado, sempre que um novo produto estiver disponível, a loja pode enviar inúmeros e-mails (que podem ser considerados spam) para todos os clientes. Isto pouparia alguns clientes de intermináveis viagens à loja, incomodando, por outro lado, os clientes que não estão interessados em novos produtos.

 

 

O objeto em estado de interesse é chamado geralmente de sujeito, mas como ele também notificará outros objetos sobre mudanças do seu estado, chamamo-lo editor. Todos os outros objetos que desejam seguir as alterações são chamados de assinantes. O padrão Observer propõe adicionar um mecanismo de subscrição à classe editor para que objetos individuais possam subscrever ou cancelar a subscrição de um fluxo de eventos provenientes desse editor.

Usaremos como exemplo a compra de um Tesla. Imagine que temos alguns clientes interessados em comprar um Tesla com um preço máximo de 100 000,00 €, mas não estão desejosos para gastar tanto dinheiro, por isso desejam ser notificados quando o preço baixar. Podemos usar o padrão Observer para resolver esse problema. Criamos 2 interfaces para definir os métodos que as nossas classes devem implementar: ISubject e IObserver.

Criamos uma classe Tesla que implementa a classe ISubject. Este será o Editor mencionado acima, que tem uma lista de assinantes, o nome e preço como propriedades. Assim, sempre que o preço é atualizado, notificamos os nossos assinantes.

Criamos uma classe TeslaClient que tem o nome e wantedPrice e implementa o método Update do IObserver, que atualmente redige na consola que o preço do Tesla está abaixo/igual ao wantedPrice.

Abaixo está o nosso método principal que inicializamos com uma lista de clientes interessados no Tesla, e criamos um assinante/editor que inclui os seus nomes e preços. Conectámos os nossos subscritores ao assinante e, finalmente, atualizamos o preço do Model X. John Doe deve ser o único cliente a ser notificado porque o 85000 < 90000.

E o resultado é:

Padrão Builder 

Um padrão Builder deve ser usado apenas para criar objetos complexos, por exemplo, quando a criação de um objeto deve estar separado da sua montagem, tal como acontece em árvores ou casas. Ele permite criar diferentes tipos e representações de um objeto com o mesmo código de construção.
Imagine um objeto complexo que requer uma inicialização tediosa e minuciosa de vários campos e objetos. Esse código de inicialização geralmente encontra-se num construtor bastante complexo com muitos parâmetros, ou pior, espalhado pelo código do cliente. O padrão Builder propõe separar o código de construção do objeto da sua própria classe e metê-lo em objetos separados chamados construtores.

O próximo ilustra uma possível maneira de implementar o padrão Builder.

Padrão Factory Method 

O Factory Method é um padrão de design que define uma interface para criar um objeto, mas deixa para as classes que implementam a interface decidir que classe instanciar. O padrão Factory permite que uma classe adie a instanciação para outras subclasses. O padrão Factory é usado para substituir os construtores de classe e abstrair o processo de criação do objeto para que o tipo do objeto instanciado possa ser determinado em tempo de execução.

Imagina que estás a criar uma aplicação para gestão de logística. A primeira versão da tua aplicação só pode lidar com remessas que envolvem camiões. Portanto, a maioria do teu código encontra-se na classe Truck. Depois de algum tempo, a tua aplicação torna-se bastante popular. Todos os dias, recebes dezenas de pedidos de empresas de transporte aquaviário para integrar a logística do envio aquaviário à aplicação. Isto requer alterações em toda a base do código. E se mais tarde decidired adicionar outro tipo de transporte à aplicação, provavelmente terád que fazer todas essas alterações novamente.
O padrão de Factory Method sugere que substituas as chamadas diretas para a criação do objeto (usando o novo operador) por chamadas para um método Factory especial. Não te preocupe: os objetos serão criados com o novo operador, mas serão invocados a partir do método Factory. Os objetos retornados de um método Factory são geralmente chamados de produtos.

Primeiro declaramos os tipos de Veículo: Truck and Ship.

Então criamos uma interface que contém os métodos que os Veículos devem implementar:

Logo após, devemos implementar a interface nas classes Vehicles:

A seguir, criamos uma classe abstrata que contém um método abstrato para ser substituído nas classes que estendem essa classe abstrata:

Aqui estendemos a classe TransportFactory e sobrescrevemos o método e retornamos a classe correta pelo tipo do argumento do veículo:

Aqui instanciamos os nossos métodos de transporte:

Resultado:

 

Conclusão

Como vimos neste artigo, o uso de padrões de design no desenvolvimento oferece variados benefícios. Eles podem melhorar bastante o código, torná-lo mais fácil de entender e resolver problemas que, de outra forma, seriam mais difíceis de resolver. Também devemos ter em mente que toda a vez que usamos padrões de design, tornamos mais fácil para outros programadores pegarem no nosso código, realizarem debug ou adicionar-lhe novo código.

É claro que os padrões de design não são nada que os programadores possam simplesmente copiar e colar no seu código, mas a ideia geral de cada um deles pode ser adaptada para responder às necessidades de uma ampla variedade de situações. Os padrões são soluções experimentadas e testadas e, portanto, opções confiáveis ​​para resolver todo o tipo de problemas usando o paradigma orientado a objetos. Eles também facilitam a comunicação com os teus colegas de equipa, pois é uma “linguagem” comum para programadores. Pode simplesmente dizer: “Use um singleton para esta tarefa” e todos saberão a que te referes.

Por estas razões, podemos supor que a utilização de padrões de design em empresas profissionalmente irá beneficiar muito o desenvolvimento dos teus produtos.

NÃO PERCA NENHUMA HISTÓRIA!Junte-se à nossa comunidade em crescimento e seja inspirado pelo nossos artigos.
Sem brincadeiras, sem jogos, sem publicidade. Somente um clique para subscrever.
ENPT
lang
Load-chatbot