0

I have something bother me in the understanding of polymorphism (vs role):
Note: I am using rails (but it's a general question)

I have 4 models:

  • User
  • Pro
  • Customer
  • Company

There is a polymorphic association (profileable) between Pro/Customer and User [because Pro has many more fields for the registration process than Customer].

User is used for the session (I am using the devise gem)

Basically something very similar to this writing is used:

http://jeffsaracco.com/blog/2012/03/04/ruby-on-rails-polymorphic-user-model/

Company association

  • Now, I want to create my association with Company.
  • By now, only Pro has one Company (but I figure it could be other profileable_type which can also have a Company).
  • Customer doesn't have a Company.

In that case which association is preferable:

Approach 1

  • User has_one Company
  • Company belongs_to User
  • and manage authorization (with a gem like cancan) to permit company creation only for Pro (with my profileable_type field in the user table)

or:

Approach 2

  • Pro has_one Company
  • Company belongs_to Pro
  • So in that case I don't need any authorization management anymore (User (and by extension Customer) cannot create company by default)

Looking for pros/cons of the 2 approaches. I am pretty sure the first one is the best solution as I have a user_id in my company table which would be more generic and expandable than having a pro_id.. I would be pleased to be sure. Thanks!

  • In Approach 2 do you mean "Company belongs to Pro" ? – glenatron Jul 19 '13 at 13:06
1

(Coming from C#/Java, hopefully you can translate back to rails:) Approach 1 is the way to go. User needs to be able to take a company, return a company, and let the code using the class know whether the first method stores anything and the second returns anything but null. Customer would do nothing in the first method, return null from the second, and the last (third) method would return false. Pro would actually save and return a company and return true for the third method.

This means you can pass around and work with User and forget about Pro's and Customer's most of the time. And you can add in other subclasses (models?), some of which have companies and some of which don't, and all your code using User will still work without a change. Add in a few other concepts besides companies and the benefits multiply.

Expanding:

User should not actually have the functionality to take a company, it just needs to look like it can so code doesn't always have to worry about whether or not a User is a Pro. At some point a User may be asked if it can handle a company, and a Pro will say "yes" and and a Customer "no". In C#/Java (and I'm hoping this translates somehow to rails) a call to "SetCompany" would probably throw an exception. (A null return from "GetCompany" might be better than an exception.)

If another class, A, is created that can have a company, extending Pro is a good idea. (Or having Pro and A extend a third abstract (or not) class that has the code to handle a company.) But this is only visible inside the new class. All the code written to handle User objects does not need to know about it, which is the whole point. All you're really doing is putting the code to handle the companies in one place (DRY). Nobody ever needs to ask if they've got an instance of a Pro. They just ask a User object (extension unknown) if it can handle a company.

And you might someday need a User extension that handles companies, but in such a different way you don't want to use the Pro code at all. With User "handling" companies, this can all be done transparently.

  • I like your answer because it's the way I think ;) However, one of my collegue told me about specialization and thinks that Pro should have the relationship (approach 2) and, if another class need to have a company, they should inherit from Pro. Now, I am a bit confused with this.. I would be very grateful if you could just edit your answer to have a short point of view from you on this. Thank you! – benoitr Jul 19 '13 at 15:12
  • @benoitr: See my addition. I was just going to add a comment, but it didn't quite fit. – RalphChapin Jul 19 '13 at 15:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.