Padrões de projeto - Desenho-1-2018-G-6/docs GitHub Wiki

Data Versão Modificação Autor
09/05/2018 1.0 Inicialização do documento Lucas Malta
10/05/2018 1.1 Inserção do State no documento Thalisson Melo
14/05/2018 1.2 Descrição do padrão composite Lucas Martins
14/05/2018 1.3 Descrição do padrão strategy Guilherme Augusto
14/05/2018 2.0 Descrição dos padrões grasp identificados Lucas Martins


Sumário

Padrões de Projeto GOF


Padrões de Projeto GOF

Este documento tem como objetivo explicitar os padrões de projeto utilizados nesta aplicação nos níveis de modelagem e de implementação, bem como a motivação para o uso dos mesmos.

1. Decorator

O padrão de projeto decorator visa anexar responsabilidades adicionais a um objeto de modo dinâmico. Desse modo, um objeto pode ter novos atributos e/ou métodos sem a necessidade de deletá-lo/recriá-lo.

Esse padrão foi introduzido em nossa aplicação no contexto de Usuário - Administrador. Um usuário, ao ser criado, contém os atributos e métodos somente de um usuário comum. Contudo, esse mesmo usuário pode adquirir o status de um administrador, dada as motivações do dono da aplicação. Sendo assim, é necessário que, dinamicamente, esse usuário adquira o status de administrador, herdando assim os atributos e métodos relacionados ao mesmo. Outros padrões poderiam ser aplicados, como o facade ou até mesmo um padrão de criação como o builder, mas achamos a ideia do Decorator mais interessante, pela possibilidade de adicionar/remover métodos e atributos dinamicamente à um objeto.

Dada as limitações do framework/linguagem, a aplicação do padrão decorator teve algumas mudanças para que o mesmo se adequa-se ao ambiente. A primeira limitação se refere à maneira que o decorator injeta novas "subclasses" à classe principal. Como a herança do rails é limitada (Além de não se existir uma classe abstrata propriamente dita), tivemos que optar por utilizar um campo chamado "user_type". Esse campo (String), contém os tipos de decorators que estão sendo adicionados. Na aplicação atual, só temos um tipo de decorator, o admin. Mas caso houvesse mais, poderiamos adicionar ao user_type: "admin manager seller", que o usuário obteria os métodos e atributos de manager e seller.

  create_table "users", force: :cascade do |t|
    t.string   "first_name"
    t.string   "last_name"
    t.string   "email"
    t.string   "cpf"
    t.date     "birth_date"
    t.string   "gender"
    t.string   "phone"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
    t.string   "password_digest"
    t.string   "user_type"
  end

Adicionado o tipo de usuário no campo, o mesmo "herda" os métodos e atributos da classe definida. Desse modo, definimos:

class UserDecorator < Draper::Decorator
  include Draper::LazyHelpers
  delegate_all

  def show_sidebar
    if current_user.user_type.include? "admin"
      render :partial => "users/admin_sidebar"
    end
  end

  def show_edit_buttons
    if current_user.user_type.include? "admin"
      return true
    else
      return false
    end
  end

Esses 2 métodos são para usuários que foram decorados com a classe de Admin. Esses usuários podem utilizar os métodos show_sidebar e show_edit_buttons, que são métodos que liberam o uso de outras funcionalidades específicas para administrador, como adição/remoção de produtos.

2. State

O padrão de projeto State visa armazenar o estado de um objeto da aplicação para que este defina uma mudança no comportamento do objeto de acordo com seu o estado.

Este padrão foi inserido na nossa aplicação no contexto do estado do carrinho, pois este tem um comportamento diferente se estiver cheio ou vazio, e futuramente terá o estado de em pagamento e de pagamento finalizado, cada um desses estados resulta em mostar para o usuário diferentes informações.

Se não houver itens no carrinho a seguinte mensagem aparece:

"Seu carrinho está vazio, por farvor, volte e adicione produtos."

se houver produtos no carrinho eles são listados.

Esse padrão é aplicado da seguinte forma:

Há uma classe mãe chamada Cart que possui o método "status", o qual é sobrescrito pelas classes filhas InProgressState e EmptyState. Esse método sobrescrito retornam uma string com o estado atual, e esse estado define o comportamento do objeto do carrinho.

OrderStatus class:


class Cart < ActiveRecord::Base

  def status
    nil
  end

end

InProgressState class:


class InProgressState < Cart

    def status
        "In Progress"
    end
end

EmptyState:


class EmptyState < Cart

    def status
        "Empty"
    end
end

Diagrama:

Diagrama

Diagrama

3. Composite

No contexto abordado pelo projeto, muitas vezes um produto a ser adquirido pelo cliente é composto de outros produtos. Um exemplo disso é um computador, que pode ser montado utilizando diversas peças e componentes diferentes. Para representar isto na aplicação, utilizou-se o padrão de projeto GOF Composite.

O Composite consiste em numa composição de objetos em uma árvore de estruturas, representando uma hierarquia de todo-parte, tratando assim um objeto como a composição de vários objetos de forma homogênea.

O padrão, como se pode ver no diagrama abaixo, consiste em uma classe Setup, que serve como interface para o Composite. Ela permite que a classe Category mantenha instâncias de objetos da classe leaf, classe Product no caso, ou da própria classe Category.

Composite

Diagrama

A aplicação deste padrão permite que o usuário construa seus produtos de forma personalizada, e compre o item mais adequado para suas necessidades.

4. Strategy

Relacionado ao projeto, existe a ideia de que, ao cliente finalizar a compra dos produtos desejados, ele escolhe a forma de pagamento desejada, podendo, no contexto do ecommerce Mamid, ser pagamento por cartão de crédito ou pagamento pela plataforma do PagSeguro.
O Strategy, no contexto abordado, permite de maneira simples, a variação dos algoritmos utilizados na resolução de um determinado problema, que, no caso, é em relação ao pagamento.
A estratégia de ramificar os tipos de pagamento, criando classes para cada uma, diminui o acoplamento do sistema, pois ao realizar a herança entre cada estratégia, evita a composição entre a classe pagamento e a classe anteriormente citada no diagrama anteriormente criado, inerente a tipo de pagamento.

captura de tela de 2018-05-14 08-47-43

Padrões de Projeto GRASP

1. Criador

Este padrão é aplicado quando uma classe é responsável por criar instâncias de outra classe.

Em geral, uma classe B deve ser responsável por criar instâncias de classe A se uma, ou preferencialmente mais, das seguintes afirmações se aplicam:

  • Instâncias de B contêm ou agregam instâncias de A;
  • Instâncias de B gravam instâncias de A;
  • Instâncias de B utilizam de perto instâncias de A;
  • Instâncias de B têm as informações de inicialização das instâncias de A e passam isso na criação.

Este padrão é aplicado em várias partes do sistema. Um exemplo de aplicação é na relação entre Product e Setup.

2. Especialista da Informação

Especialista na Informação é uma abordagem genérica que visa atribuir a responsabilidade de fazer ou conhecer algo ao "especialista", ou seja, à classe que possui a informação necessária para cumprir tal responsabilidade.

Este padrão foi aplicado nas mesmas classes onde existe o padrão Criador.

3. Alta Coesão

Alta coesão é um padrão que tenta manter os objetos adequadamente focados, gerenciáveis ​​e compreensíveis. Significa que as responsabilidades de um determinado elemento do sistema estão altamente relacionadas e focadas em um contexto específico. Isto evita a classes que possuem várias responsabilidades distintas, não relacionadas, o que resulta em um sistema difícil de compreender e com a manutenibilidade baixa.

Todo o sistema foi desenvolvido visando a Alta Coesão, sempre delegando responsabilidades específicas e relacionadas para as classes, com o objetivo de prover uma maior manutenibilidade e tornar a compreensão do sistema mais fácil.

4. Baixo Acoplamento

O acoplamento é uma medida de quão forte um elemento está conectado, tem conhecimento ou depende de outros elementos. O padrão Baixo Acoplamento tem o objetivo de garantir uma menor dependência entre classes, maior potencial de reutilização de software, e permitir que mudanças em uma classe tenha menor impacto em outras.

Assim como a Alta Coesão, o Baixo Acoplamento foi sempre considerado durante a implementação do sistema, para garantir a manutenibilidade, diminuindo o custo de mudanças no sistema, e a reutilização de código.

5. Polimorfismo

O Polimorfismo consiste em definir a variação dos comportamentos com base em superclasses para o qual essa variação ocorre. Em outras palavras, este padrão é aplicado quando subclasses de uma superclasse são capazes de invocar métodos, que comportam-se de maneira diferente para cada classe derivada. Este padrão fornece mais flexibilidade ao sistema, pois permite que objetos do mesmo "tipo" adquiram comportamentos diferentes.

O Polimorfismo é a base para quase todos os Padrões de Projeto GOF, e pode ser encontrado em muitas partes do Mamid.

6. Invenção Pura

Uma Invenção Pura é uma classe artificial que não representa um conceito no domínio do problema, especialmente feito para conseguir baixo acoplamento, alta coesão e o potencial de reutilização.

7. Controlador

O padrão Controlador atribui a responsabilidade de lidar com os eventos do sistema para uma classe que representa a um cenário de caso de uso do sistema global.

Uma aplicação muito famosa deste padrão é encontrada no Padrão Arquitetural MVC. Como o sistema em questão é baseado na arquitetura MVC, pode-se dizer que o projeto foi "usuário" do padrão Controlador.