1

I'm currently working with some code that IMO has abused ruby's mixin features. Given that I'm new to ruby, I wonder if MO in IMO is correct or not.

My primary question is what makes more sense (and if there are any major technical differences between them):

E.g.

1

to create Helper singleton classes (or Modules with class methods) to keep all my "helper" functions.

module Helper
  def foo(); p 'foo'; end  # instance method
end

class User
  include Helper     # foo() is now a method of User

  def initialize()
    foo()            # included, invoked on User class object
  end

end

------- OR -------

2

create mixin modules that contain these functions and include them everywhere I need them.

module Helper
  def self.foo(); p 'foo'; end  # class method
end

class User
  
  def initialize()
    Helper.foo()            # Invoked on Helper module object
  end

end

An example of the way it's abused in my case:

module ComplexMathPerformer
  def perform_complex_math(); p 'perform_complex_math()'; end
end

module Renderer
  def render_3d_object()
    perform_complex_math() # from ComplexMathPerformer, which is NOT included here.
  end
end

module Game
  include ComplexMathPerformer
  include Renderer

  def play()
    render_3d_object()
  end

end

This code works because Game includes both Renderer and ComplexMathPerformer. But is a nightmare for someone who's looking at ComplexMathPerformer and trying to figure out where the hell is is foo() defined and how is it accessible in ComplexMathPerformer.

Logically it makes more sense to include ComplexMathPerformer but that's skipped because Game already includes ComplexMathPerformer (for it's own purposes). Making it a mess.

Kashyap
  • 159
  • 5
  • In the example you provide in #2, the dependency between Helper1 and Helper2 exists because you are declaring foo() inside Helper1. But you also allow that foo() is defined in the class itself. – pietromenna Feb 11 '16 at 17:00
  • @pietromenna I suppose you meant "But you ***CAN*** also allow that foo() is..." - In theory yes, but the in practice `Helper1 & 2` both have 7-10 related methods each (addressing different responsibilities) with a total of 150-200 LOC so it makes sense to keep them apart. – Kashyap Feb 11 '16 at 17:13
  • You got me right! I have one additional question: You separated Helper1 and Helper2 because who should use the methods of each one is different, right? For example, you have class A which only needs to implement `Helper1` methods, but class B does not need those, but only the ones from `Helper2`. The problem I see in the example is **coupling** between `Helper1` and `Helper2` modules, but not an abuse of mixin (which would be an abuse on abstractions) – pietromenna Feb 11 '16 at 17:20
  • @pietromenna It's more a dependency between `Helpers`, I updated the example to make a bit more clear. – Kashyap Feb 11 '16 at 17:56
  • Yeah, the class method approach is more immediately comprehensible but including a module is a fundamental thing that is frequently done, so its hard to say its abuse per se. – max pleaner Dec 24 '16 at 03:10

1 Answers1

2

By checking the examples, IMHO, there is no abuse of the mixin feature of Ruby, but coupling between the modules ComplexMathPerformer and Renderer.

Mixin abuse is generally felt by developers when too much abstraction exists. Mixins should be used when you need to share behavior between a couple of classes.

Modules are usually used when you need to create functions more as functions (methods without state).

Maybe ComplexMathPerformer logic should be inside the game, or in another class which the Game consumes and not as a Module in your case.

pietromenna
  • 1,148
  • 5
  • 10