Whats a good ruby idiom for breaking up a large class into modules?

I have a large class with lots of methods and it's starting to get a bit unorganized and hard to navigate. I'd like to break it up into modules, where each module is a collection of class and instance methods. Perhaps something like this:

UPDATE: I've now realized that this is a pretty poor example. You probably wouldn't want to move validations or attributes out of the core class.

class Large
  include Validations
  include Attributes
  include BusinessLogic
  include Callbacks
end

After reading Yehuda's post about Better Ruby Idioms, I'm curious how others are tackling this problem. Here's the two methods I can think of.

First Method

module Foo
  module Validations
    module ClassMethods
      def bar
        "bar"
      end
    end

    module InstanceMethods
      def baz
        "baz"
      end
    end
  end

  class Large
    extend Validations::ClassMethods
    include Validations::InstanceMethods
  end
end

Second Method

module Foo
  module Validations
    def self.included(base)
      base.extend ClassMethods
    end

    module ClassMethods
      def bar
        "bar"
      end
    end

    def baz
      "baz"
    end
  end

  class Base
    include Validations
  end
end

My questions are:

  • Is there a better way to do this?
  • How do you get a one-liner module mixin for a set of class/instance methods with the least amount of magic?
  • How do you namespace these modules to the base class without namespacing the class itself?
  • How do you organize these files?

  • Breaking a class into modules, while tempting (because it's so easy in Ruby), is rarely the right answer. I usually regard the temptation to break out modules as the code's way of telling me it wants to be split into more tightly-focussed classes. A class that's so big you want to break it into multiple files is pretty much guaranteed to be violating the Single Responsibility Principle.

    EDIT: To elaborate a bit on why breaking code into modules is a bad idea: it's confusing to the reader/maintainer. A class should represent a single tightly-focussed concept. It's bad enough when you have to scroll hundreds of lines to find the definition of an instance method used at the other end of a long class file. It's even worse when you come across an instance method call and have to go looking in another file for it.


    After doing what Avdi said, these are the things I would do before putting anything into a module:

  • Whether this module can or will be used in any other class?
  • Would it make sense to extract the functionality of these modules into a different or base class?
  • If the answer for 1 is no and 2 is yes then IMHO that indicates to better have a class rather a module.

    Also, I think putting attributes in a module is conceptually wrong because classes never share their attributes or instance variables or in other words their internal state with any other class. The attributes of a class belongs to that class only.

    Business logics do definitely belong to the class itself and if the business logic of class A has some common responsibilities with class C then that needs to be extracted into a base class to make it clear instead of just putting it into a module.


    Although including different modules will work, it is generally more troublesome than simply reopening the class in multiple places.

    There is a (very simple) gem that you can use to makes this as pretty as can be: concerned_with

    Example (from the readme)

    # app/models/user.rb
    class User < ActiveRecord::Base
      concerned_with :validations,
                     :authentication
    end
    
    # app/models/user/validations.rb
    class User < ActiveRecord::Base
      validates_presence_of :name
    end
    
    #app/models/user/authentication.rb
    class User < ActiveRecord::Base
      def self.authenticate(name, password)
        find_by_name_and_password(name, password)
      end
    end
    
    链接地址: http://www.djcxy.com/p/30684.html

    上一篇: 使用名称空间和本地名称

    下一篇: 把一个大班分成几个模块,这是一个好的ruby成语吗?