Using Class vs Module for packaging code in Ruby

2019-07-09 05:20发布

Let's say I have a bunch of related functions that have no persistent state, say various operations in a string differencing package. I can either define them in a class or module (using self) and they can be accessed the exact same way:

class Diff
  def self.diff ...
  def self.patch ...
end

or

module Diff
  def self.diff ...
  def self.patch ...
end

I can then do Diff.patch(...). Which is 'better' (or 'correct')?

The main reason I need to group them up is namespace issues, common function names are all used elsewhere.

Edit: Changed example from matrix to diff. Matrix is a terrible example as it does have state and everyone started explaining why it's better to write them as methods rather than answer the actual question. :(

5条回答
男人必须洒脱
2楼-- · 2019-07-09 05:28

As a matter of form, the Module is more correct. You can still create instances of the class, even if it has only class methods. You can think of a module here as a static class of C# or Java. Classes also always have the instance related methods (new, allocate, etc.). Use the Module. Class methods usually have something to do with objects (creating them, manipulating them).

查看更多
乱世女痞
3楼-- · 2019-07-09 05:29

Ruby Modules are used to specify behaviour, pieces of related functionality.

Ruby Classes are used to specify both state and behaviour, a singular entity.

There is a maxim in software design that says that code is a liability, so use the less code possible. In the case of Ruby, the difference in code lines is cero. So you can use either way (if you don't need to save state)

If you want to be a purist, then use a Module, since you won't be using the State functionality. But I wouldn't say that using a class is wrong.

As a trivia info: In Ruby a Class is a kind of Module.

http://www.ruby-doc.org/core-1.9.3/Class.html

查看更多
一夜七次
4楼-- · 2019-07-09 05:32

The following also works

Matrix = Object.new

def Matrix.add ...
def Matrix.equals ...

That's because so-called "class methods" are just methods added to a single object, and it doesn't really matter what that object class is.

查看更多
再贱就再见
5楼-- · 2019-07-09 05:45

The main difference between modules and classes is that you can not instantiate a module; you can't do obj = MyModule.new. The assumption of your question is that you don't want to instantiate anything, so I recommend just using a module.

Still you should reconsider your approach: rather than using arrays of arrays or whatever you are doing to represent a Matrix, it would be more elegant to make your own class to represent a matrix, or find a good class that someone else has already written.

查看更多
仙女界的扛把子
6楼-- · 2019-07-09 05:54

In your two examples, you are not actually defining methods in a Class or a Module; you are defining singleton methods on an object which happens to be a Class or a Module, but could be just about any object. Here's an example with a String:

Diff = "Use me to access really cool methods"

def Diff.patch
  # ...
end

You can do any of these and that will work, but the best way to group related methods is in a Module as normal instance methods (i.e. without self.):

module Diff
  extend self  # This makes the instance methods available to the Diff module itself
  def diff ... # no self.
  def patch ...
end

Now you can:

  • use this functionality from within any Class (with include Diff) or from any object (with extend Diff)
  • an example of this use is the extend self line which makes it possible to call Diff.patch.
  • even use these methods in the global namespace

For example, in irb:

class Foo
  include Diff
end
Foo.new.patch # => calls the patch method
Diff.patch    # => also calls Diff.patch
include Diff  # => now you can call methods directly:
patch         # => also calls the patch method

Note: the extend self will "modify" the Diff module object itself but it won't have any effect on inclusions of the module. Same thing happens for a def self.foo, the foo won't be available to any class including it. In short, only instance methods of Diff are imported with an include (or an extend), not the singleton methods. Only subclassing a class will provide inheritance of both instance and singleton methods.

When you actually want the inclusion of a module to provide both instance methods and singleton methods, it's not completely easy. You have to use the self.included hook:

module Foo
  def some_instance_method; end

  module ClassMethods
    def some_singleton_method; end
  end

  def self.included(base)
    base.send :extend, ClassMethods
  end

  def self.will_not_be_included_in_any_way; end
end

class Bar
  include Foo
end
# Bar has now instance methods:
Bar.new.some_instance_method  # => nil
# and singleton methods:
Bar.some_singleton_method   # => nil
查看更多
登录 后发表回答