Refactoring ActiveRecord models with a base class versus a base module

前端 未结 5 1313
执念已碎
执念已碎 2021-02-04 08:22

Class A and B are identical:

class A < ActiveRecord::Base
 def foo
  puts \"foo\"
 end
end

class B < ActiveRecord::Base
 def foo
  puts \"foo\"
 end
end
<         


        
相关标签:
5条回答
  • 2021-02-04 08:56

    You have more flexibility with the module. The module's intent is to span across different types of classes. With the other method you are locking yourself into Base. Other than that, there isn't much difference.

    Ruby's answer to multiple inheritance is mixins. Since your classes are already inheriting from Rails specific classes, they can no longer inherit from your custom classes.

    So your choice is to chain together in a long chain, or use a mixin which is much cleaner, and easier to understand.

    0 讨论(0)
  • 2021-02-04 09:00

    The module gives you more flexibility in that 1) you can only inherit from one class, but you can include multiple modules, and 2) you can't inherit from a base class without inheriting its superclasses, but you can include a module all by itself (e.g. you might want to add the "foo" method to another class that isn't an active record model).

    Another difference is that within the methods in the class Base you could call things from ActiveRecord::Base, but you couldn't do that from the module.

    0 讨论(0)
  • 2021-02-04 09:07

    Both of these methods will work. When deciding to use a module or a class, the question I have is does the class fit into the object hierarchy, or are these just methods I am looking to reuse. If I am just trying to factor out common code for DRY reasons, that sounds like a module. If there really is a class that fits into the hierarchy that makes sense on its own, I use a class.

    Coming from a Java background, it is refreshing I can choose to make these decisions.

    0 讨论(0)
  • 2021-02-04 09:10

    There's a fundamental difference between those two methods that all the other answers are missing, and that's rails' implementation of STIs (Single Table Inheritance):

    http://api.rubyonrails.org/classes/ActiveRecord/Base.html (Find the "Single Table Inheritance" section)

    Basically, if you refactor your Base class like this:

    class Base < ActiveRecord::Base
      def foo
        puts "foo"
      end
    end
    
    class A < Base
    end
    
    class B < Base
    end
    

    Then, you are supposed to have a database table called "bases", with a column called "type", which should have a value of "A" or "B". The columns on this table will be the same across all your models, and if you have a column that belongs to only one of the models, your "bases" table will be denormalized.

    Whereas, if you refactor your Base class like this:

    Module Base
      def foo
      puts "foo"
     end
    end
    
    class A < ActiveRecord::Base
     include Base
    end
    
    class B < ActiveRecord::Base
     include Base
    end
    

    Then there will be no table "bases". Instead, there will be a table "as" and a table "bs". If they have the same attributes, the columns will have to be duplicated across both tables, but if there are differences, they won't be denomarlized.

    So, if one is preferable over the other, yes, but that's specific to your application. As a rule of thumb, if they have the exact same properties or a big overlap, use STI (1st example), else, use Modules (2nd example).

    0 讨论(0)
  • 2021-02-04 09:18

    It depends on what you are really trying to do.

    1. Overriding or adding methods to ActiveRecord::Base: Do this if you want every ActiveRecord model in your app to respond_to foo.
    2. Subclass ActiveRecord::Base, and have every model inherit from your subclass: Achieves the same as 1, but every model in your app needs to extend an unconventional class, so why go through the trouble.
    3. include module: This works great if only some number of models need access to foo. This is pretty much what all those acts_as_<whatever> plugins do.

    Bottom line, if you want every single model to have a different behavior to what ActiveRecord::Base already provides, use option 1. If only a handful of your models require the behavior, create a module and include it in your models (option 3).

    0 讨论(0)
提交回复
热议问题