Desenvolvimento

18 dez, 2018

Arquitetura e desenvolvimento de software – Parte 05: Prototype

Publicidade

Fala, galera!

Já foram quatro artigos – restam 19 + extras! E hoje abordaremos o pattern Prototype. Vamos lá?

Prototype

Prototype é um padrão que faz parte do grupo de padrões Criacionais e que permite a criação de novos objetos a partir de um modelo original, ou como o próprio nome indica, um protótipo, que é clonado.

Entre suas utilidades, encontramos:

  • Evitar que as subclasses que criam objetos funcionem como o padrão Abstract Factory;
  • Evitar criar um novo objeto utilizando a palavra new, o que diminui o custo de memória;
  • Em vez de o cliente implementar um código que utiliza o operador new, este utiliza o método Clone(), presente no protótipo e o método de uma fábrica (Factory Method ou Abstract Factory) que fica encarregada de clonar o novo objeto;

No livro, ele foi descrito com os seguintes elementos:

  • Prototype: uma classe que declara uma interface para objetos capazes de clonar a si mesmo;
  • PrototypeConcreto: implementação de um Prototype;
  • Cliente: cria um novo objeto através de um Prototype que é capaz de clonar a si mesmo;

Analisando de forma clara, cada objeto é, ele próprio, um factory especializado em construir objetos iguais a si mesmo. O Prototype é utilizado frequentemente em linguagens estaticamente tipadas, como C++, Java e C#, e menos frequentemente utilizadas em linguagens dinamicamente tipadas, como Smalltalk.

O padrão Prototype exige a implementação de uma operação de clonagem em cada uma das classes concretas do protótipo. Essa tarefa pode ser inconveniente no caso do reaproveitamento de classes preexistentes que não possuem tal operação, ou mesmo complexa, se for considerada a possibilidade de existirem referências circulares nos atributos de um objeto.

Utilização

O padrão Prototype é aplicado quando existe a necessidade de clonar, literalmente, um objeto. Ou seja, quando a aplicação precisa criar cópias exatas de algum objeto em tempo de execução, esse padrão é altamente recomendado e pode ser utilizado em sistemas que precisam ser independentes da forma como os seus componentes são criados, compostos e representados.

O padrão Prototype pode ser útil em sistemas com as seguintes características:

  • Sistemas que utilizam classes definidas em runtime;
  • Sistemas que utilizam o padrão Abstract Factory para criação de objetos. Neste caso, a hierarquia de classes pode se tornar muito complexa e o padrão Prototype pode ser uma alternativa mais simples por realizar a mesma tarefa com um número reduzido de classes;
  • Sistemas que possuem componentes cujo estado inicial possui poucas variações e onde é conveniente disponibilizar um conjunto preestabelecido de protótipos que dão origem aos objetos que compõem o sistema.

Algumas vezes, padrões criacionais podem ser “competidores”: existem casos em que o Prototype ou Abstract Factory podem ser utilizados apropriadamente. Em outros, eles são complementares: um Abstract Factory pode conter um set de Prototypes no qual clona e retorna o produto de objetos.

Quando utilizamos o Spring framework, por exemplo, um desenvolvedor pode configurar um Bean como “Prototype”. Essa configuração faz com que cada uma das referências a um Bean aponte para uma instância diferente. O comportamento padrão, ou Singleton, define que todas as referências a um Bean apontem para a mesma instância de uma classe.

Uma das principais vantagens de sua utilização é quando a inicialização de um objeto pode se tornar custosa, e você quer fazer algumas pequenas variações ao inicializar. Nesse contexto, o Prototype pode então evitar a criação “do zero” de novos objetos.

Ficha resumo

Vamos à ficha resumo do pattern, onde daremos um exemplo em C# e teremos suas vantagens e desvantagens:

  • Nome: Prototype.
  • Objetivo/intenção: permitir a criação de novos objetos a partir de um modelo original, ou como o próprio nome indica, um protótipo, que é clonado.
  • Motivação: evitar que as subclasses que criam objetos funcionem como o padrão Abstract Factory ou evitar criar um novo objeto utilizando a palavra new, o que diminui o custo de memória.
  • Aplicabilidade: o padrão Prototype é aplicado quando existe a necessidade de clonar, literalmente, um objeto. Ou seja, quando a aplicação precisa criar cópias exatas de algum objeto em tempo de execução, este padrão é altamente recomendado e também pode ser utilizado em sistemas que precisam ser independentes da forma como os seus componentes são criados, compostos e representados.
  • Estrutura: abaixo, um exemplo de estrutura onde temos um classe Prototype, que declara uma interface para objetos capazes de clonar a si mesmo, a class PrototypeConcreto, que é uma implementação de Prototype e a classe Cliente, que um novo objeto através de um Prototype que é capaz de clonar a si mesmo.

  • Consequências: Prototype facilita bastante quando temos classes que precisam simplesmente ser clonadas de forma livre e que definam seu comportamento em runtime, mas exigem a implementação de uma operação de clonagem em cada uma das classes concretas do protótipo. Essa tarefa pode ser inconveniente no caso do reaproveitamento de classes preexistentes que não possuem tal operação, ou mesmo complexa, se for considerada a possibilidade de existirem referências circulares nos atributos de um objeto.
  • Implementações: temos abaixo um código em C# que demonstra o uso do padrão Prototype na produção de objetos que representam cores, onde o ColorManager possui as as definições dessas cores e apenas clona elas para uso posterior.
class MainApp
{
  static void Main()
  {
    ColorManager colormanager = new ColorManager(); 

    colormanager["red"] = new Color(255, 0, 0);
    colormanager["green"] = new Color(0, 255, 0);
    colormanager["blue"] = new Color(0, 0, 255);
 
    colormanager["angry"] = new Color(255, 54, 0);
    colormanager["peace"] = new Color(128, 211, 128);
    colormanager["flame"] = new Color(211, 34, 20);
 
    Color color1 = colormanager["red"].Clone() as Color;
    Color color2 = colormanager["peace"].Clone() as Color;
    Color color3 = colormanager["flame"].Clone() as Color;
 
    Console.ReadKey();
  }
}
 
abstract class ColorPrototype
{
  public abstract ColorPrototype Clone();
}
 
class Color : ColorPrototype
{
  private int _red;
  private int _green;
  private int _blue;
  
  public Color(int red, int green, int blue)
  {
    this._red = red;
    this._green = green;
    this._blue = blue;
   }

  public override ColorPrototype Clone()
  {
    Console.WriteLine(
      "Cloning color RGB: {0,3},{1,3},{2,3}",
        _red, _green, _blue);
 
    return this.MemberwiseClone() as ColorPrototype;
  }
}
 
class ColorManager
{
  private Dictionary<string, ColorPrototype> _colors =
    new Dictionary<string, ColorPrototype>();
 
  public ColorPrototype this[string key]
  {
    get { return _colors[key]; }
    set { _colors.Add(key, value); }
  }
}
  • Usos conhecidos: quando utilizamos o Spring framework, por exemplo, um desenvolvedor pode configurar um Bean como “Prototype”. Essa configuração faz com que cada uma das referências a um Bean aponte para uma instância diferente. O comportamento padrão, ou Singleton, define que todas as referências a um Bean apontem para a mesma instância de uma classe.
  • Padrões relacionados: Abstract Factory, Factory Method e Singleton.

Concluindo

Este foi o padrão Prototype. Ele é bem útil em sistemas que precisamos clonar livremente os objetos e que suas definições sejam feitas em runtime. Na próxima parte desta série vamos abordar o padrão Singleton, o último do grupo de criacionais.

Como sempre, aguardo feedbacks e dúvidas de vocês! Nos vemos em breve!