具有多个角色的人的设计模式 [英] Design pattern for person with multiple roles

查看:318
本文介绍了具有多个角色的人的设计模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个基本模型Person,该模型具有一些与人相关的属性,例如姓名,地址,电话等.一个Person可以是以下的一个或多个:

I want to create a base model Person with some person related attributes like name, address, phone and so on. One Person can be one ore more of the following:

  • LoginUser,其中包含用于登录,密码,last_login等的字段...
  • CardHolder,其中包含card_id,last_entrance,...
  • 的字段
  • Supplier仅带有一个标志,表明该人是否是供应商<​​/li>
  • Recipient仅带有一个标志,表明该人是否是收件人
  • LoginUser with fields for login, password, last_login, ...
  • CardHolder with fields for card_id, last_entrance, ...
  • Supplier with just a flag whether or not the person is a supplier
  • Recipient with just a flag whether or not the person is a recipient

Ruby on Rails中是否存在常识或最佳实践设计模式来表示这种继承?如何在模型和表结构中表示它,以便可以检查Person是否为LoginUser并访问相应的字段. 在另一个项目中,我已经与STI合作,但是在这种情况下,这不是正确的模式.

Is there a common sense or best practise design pattern in Ruby on Rails to represent that inheritance? How it should be represented in the model(s) and table structure so that it is possible to check whether a Person is a LoginUser and to access the corresponding fields. In another project I worked already with STI but in this case this isn't the right pattern.

推荐答案

您正在寻找的是反向多态关联.多态关联使您可以将一个模型链接到许多不同的模型.通过反向多态关联,您可以将许多模型链接到一个模型.设置起来有些棘手,但是一旦掌握了它,就没问题了.

What you're looking for is a reverse polymorphic association. Polymorphic associations allow you to link one model to many different ones. A reverse polymorphic association allows you to link many models to one single one. They're a little tricky to set up, but once you get the hang of it it's no problem.

为完成此任务,您需要另一个模型充当Person模型与每个不同角色之间的过渡.这种过渡模型实际上是具有多态关联的模型.您的人"模型将has_many该模型,而您的各种角色模型将对其进行has_one.然后,您使用:through进行其余的必要关联,因此您的代码不会有所不同.沙赞!

In order to accomplish this, you need another model that acts as a go-between for the Person model and each of the different roles. This go-between model is the one that actually has the polymorphic association. Your Person model will has_many that model, and your various role models will has_one of it. You then use :through to make the rest of the necessary associations so your code doesn't know any different. Shazam!

这是如何使用PersonCardHolder模型执行此操作的示例.我称呼额外的模型Role是因为这似乎是一个显而易见的选择:

Here's an example of how to do it with the Person and CardHolder models. I'm calling the extra model Role because that seems like an obvious choice:

class Person < ApplicationRecord
  has_many :roles
  # Reach through the Roles association to get the CardHolders, via polymorphic :rollable.
  # Unfortunately, you can't has_one, so you'll have to enforce uniqueness in Role
  # with a validation.
  has_many :card_holders, through: :roles, source: :rollable, source_type: 'CardHolder'
end

class Role < ApplicationRecord
  belongs_to :person
  # Here is where our actual polymorphic connection is:
  belongs_to :rollable, polymorphic: true
end

class CardHolder < ApplicationRecord
  # The other side of the polymorphic connection, with has_one:
  has_one :role, as: :rollable
  # Get the person via the role, just like the inverse:
  has_one :person, through: :role
end

数据库设置如下:

class CreatePeople < ActiveRecord::Migration[5.1]
  def change
    create_table :people do |t|
      t.string :name
      # put in whatever other Person columns you need

      t.timestamps
    end
  end
end

class CreateRoles < ActiveRecord::Migration[5.1]
  def change
    create_table :roles do |t|
      t.references :person, index: true
      t.references :rollable, polymorphic: true, index: true
      t.timestamps
    end
  end
end

class CreateCardHolders < ActiveRecord::Migration[5.1]
  def change
    create_table :card_holders do |t|
      t.integer :card_id
      t.datetime :last_entrance
      # put in whatever other columns you need

      t.timestamps
    end
  end
end

使用它非常简单:

> p = Person.create(name: "Sven Reuter")
# directly add a card holder
> p.card_holders << CardHolder.create(card_id: 1, last_entrance: Time.current)
# build a role instead
> p.roles.build(rollable: CardHolder.new(card_id: 2, last_entrance: Time.current)
# get all of the roles
> p.roles

这篇关于具有多个角色的人的设计模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆