Introdução à Associações Polimórficas
Este artigo lhe dará uma maior compreensão do polimorfismo, especificamente em Ruby on Rails.
Associações polimórficas no Rails são associações um pouco mais avançadas e permitem que um modelo pertença (belongs_to
) a mais de um outro modelo usando uma única associação. Isso é útil quando você tem um modelo que pode estar associado a mais de um tipo de outro modelo.
Existem várias maneiras de definir polimorfismo em diferentes contextos. Uma definição útil, independentemente do contexto, é ‘a capacidade de um objeto exibir mais de uma forma’
- poli significa ‘muitos’ e morph significa ‘forma’.
No mundo real, pense em um smartphone. Um smartphone pode funcionar como um telefone, um navegador de internet, uma câmera, um GPS, um dispositivo de música, entre outros. Cada função determina como ele é utilizado e contribui para sua importância na vida das pessoas. Cada função é diferente, mas todas juntas fazem do smartphone um objeto essencial e multifuncional.
Polimorfismo em OOP (Programa Orientado a Objetos)
Se considerarmos nossa definição inicial de polimorfismo – a capacidade de um objeto exibir mais de uma forma – podemos relacioná-lo perfeitamente com OOP.
Em OOP, podemos usar o mesmo método para produzir resultados diferentes, passando objetos separados. Poderíamos usar condicionais para conseguir isso. No entanto, isso pode criar um código pesado e nos desviar dos princípios DRY. O polimorfismo é essencial na criação de aplicativos OOP limpos e lógicos.
Vejamos um exemplo de como o polimorfismo pode ser implementado em uma linguagem OOP como Ruby
Exemplo
Imagine que estamos desenvolvendo uma integração onde precisamos cadastrar usuários, empresas e stakeholders, todos os 3 modelos precisam de um endereço para serem cadastrados. Para evitar que a gente crie uma associação normal onde o endereço vai pertencer a todos, podemos criar uma associação polimórfica.
SEM O POLIMORFISMO: Criando os Modelos
Primeiro vou mostrar como ficaria essa associação sem o polimorfismo.
class Endereco < ApplicationRecord
has_many :users
has_many :stakeholders
has_many :companies
end
class User < ApplicationRecord
belongs_to :endereco
validates :name, presence: true
validates :email, presence: true, uniqueness: true
end
class Stakeholder < ApplicationRecord
belongs_to :endereco
validates :name, presence: true
validates :email, presence: true, uniqueness: true
end
class Empresa < ApplicationRecord
belongs_to :endereco
validates :razao_social, presence: true
validates :email, presence: true, uniqueness: true
end
Como podemos observar em cada tabela eu tive que adicionar o endereco_id
para demonstrar que stakeholder
, empresa
e user
tem uma ligação com o endereço, e preciso sempre lembrar de colocar nos modelos as associações corretas. E para cada outra tabela nova iremos sempre precisar fazer esse fluxo lembrando de criar as associações corretamente e com isso você acabaria escrevendo muitos códigos repetitivos neste processo.
Isso pode não ser um problema para aplicativos pequenos, mas à medida que seu aplicativo cresce, pode se tornar um grande problema.
COM O POLIMORFISMO: Criando os modelos
A solução para o problema acima é usar associações polimórficas no Rails. Isso permite definir um único modelo que pode pertencer a outros modelos diferentes sem a necessidade de escrever código repetido.
Migracao:
rails generate model empresa
rails generate model user
rails generate model stakeholder
Agora as nossas tabelas de empresa, user e stakeholder nao precisam mais ter o campo endereco_id
, pois na a associacao será polimórfica, vamos criá-la:
rails generate model endereco rua numero:integer owner:references{polymorphic}
rails db:migrate
Aqui estamos falando que esse owner tem uma referência para uma tabela polimórfica. Olhando agora a migration podemos ver que ele vai criar duas coisas importantes pra gente. Ele vai criar esse t.string :owner_id
e t.string :owner_type
Aqui irei atualizar os nosso modelos para adequa-los ao polimorfismo. O modelo que será polimórfico ele não precisará mais ter todas as associações e agora apenas com a linha do belongs_to :owner, polymorphic: true
qualquer outro modelo vai ter a capacidade de se ligar a este. A única diferença é que nos outros modelos teremos que retirar o belongs_to e criar a associação como has_one :endereco, as: :owner, class_name: "Endereco"
.
Obsersavação: Ele tambem funcionará para o has_many
, porém neste exemplo que eu criei eu quero que um usuário possa ter apenas um endereço.
class Endereco < ApplicationRecord
belongs_to :owner, polymorphic: true
end
class Stakeholder < ApplicationRecord
has_one :endereco, as: :owner, class_name: "Endereco"
validates :name, presence: true
validates :email, presence: true, uniqueness: true
end
class User < ApplicationRecord
has_one :endereco, as: :owner, class_name: "Endereco"
validates :name, presence: true
validates :email, presence: true, uniqueness: true
end
class Empresa < ApplicationRecord
has_one :endereco, as: :owner, class_name: "Endereco"
validates :razao_social, presence: true
validates :email, presence: true, uniqueness: true
end
Agora vamos dar uma olhada como ele realmente funciona abrindo o nosso console e criando um endereço para cada uma das minhas tabelas
$empresa = Empresa.first
$endereco = Endereco.new(rua: "Rua da Paz", numero: 123, owner: empresa)
$endereco.save
$user = User.first
$endereco = Endereco.new(rua: "Rua da Flores", numero: 342, owner: user)
$endereco.save
$stakeholder = Stakeholder.first
$endereco = Endereco.new(rua: "Av. Solar", numero: 987, owner: stakeholder)
$endereco.save
Quando a gente for criar um endereço sabendo que ele é um polimórfico, o rails vai preencher automaticamente pra gente as colunas owner_type
e owner_id
, e como podemos ver aqui, no owner_type ele preenche com o nome da tabela que é dona dele e o id do dono também.
Agora podemos recuperar o endereço da empresa, usando o empresa.endereco
Bônus
Outra forma que podemos atribuir criar o endereço é utilizando alguns métodos do Rails.
A associação has_one
, nos possibilita buildar e criar da seguinte maneira tambem:
- Antes:
$empresa = Empresa.first
$endereco = Endereco.new(rua: "Rua da Paz", numero: 123, owner: empresa)
$endereco.save
- Depois:
$empresa = Empresa.first
$empresa.build_endereco(rua: "Rua da Paz", numero: 123)
$empresa.save
ou
$empresa = Empresa.first
$endereco = empresa.build_endereco(rua: "Rua da Paz", numero: 123)
$endereco.save
ou
$empresa = Empresa.first
$empresa.create_endereco(rua: "Rua da Paz", numero: 123)
Vantagens da Associação Polimórfica
- Flexibilidade: Permite que diferentes modelos compartilhem uma única tabela de associações. Isso pode ser útil quando múltiplos modelos precisam se associar ao mesmo tipo de entidade.
- Simplificação de Estrutura: Reduz a necessidade de várias tabelas de junção, simplificando o esquema do banco de dados.
- Reutilização de Código: Promove a reutilização de código, uma vez que a lógica de associação é centralizada em um único modelo (
Endereco
). - Facilidade de Manutenção: Facilita a manutenção do código, pois qualquer mudança na lógica de associação precisa ser feita apenas uma vez.
Considerações Finais
As associações polimórficas em Ruby on Rails são uma poderosa ferramenta para desenvolvedores que buscam flexibilidade e simplicidade na modelagem de dados. Elas permitem que múltiplos modelos compartilhem uma única tabela de associações, promovendo a reutilização de código e simplificando a estrutura do banco de dados.
Ao centralizar a lógica de associação em um único modelo, como no caso da tabela de endereços, as associações polimórficas reduzem a necessidade de tabelas de junção específicas para cada relacionamento. Isso resulta em uma manutenção mais fácil, já que qualquer mudança na lógica de associação precisa ser feita apenas uma vez. Além disso, a flexibilidade oferecida por essa abordagem é particularmente útil em aplicações onde diferentes modelos precisam se associar ao mesmo tipo de entidade.
Embora as consultas possam se tornar mais complexas e seja necessário um cuidado extra para manter a integridade dos dados, existem muitas vantagens no polimorfismo.
Você pode ler mais sobre a associação polimórfica e outras maneiras de utilizar o polimorfismo nos artigos listados abaixo.