No Attributes in Objects

I just realised that I rarely define attributes in my classes (the dependency injection references are exceptions) and I’m passing everything as a parameter. So instead of this:

class Foo
  attr_accessor :list

  def do_something
    @list = []
    action_1
    action_2
  end

  private
  def action1
    @list[2] = "a"
  end

  def action_2
    @list[1] = "b"
  end
end

I have this:

class Foo
  def do_something
    list = []
    action_1(list)
    action_2(list)
  end

  private
  #   self. means that this is a class level method (static in Java)
  def self.action_1(list)
    list[2] = "a"
  end

  def self.action_2(list)
    list[1] = "b"
  end
end

Similarly to my spike workflow I realised what I’m unintentionally doing after actually having done it. I switched to this method because of the Unix philosophy: “The Unix philosophy emphasizes building short, simple, clear, modular, and extendable code that can be easily maintained and repurposed by developers other than its creators.”

More precisely: I switched to this method because I was struggling with the Unix philosophy. When I’m writing code I don’t know what the shortest and simplest entity in my code is so I put everything into a class and when it is done, I may refactor it into smaller classes. This refactoring work is very hard when I have tightly coupled methods, but when I have methods that are not coupled by attributes (except dependency injection references) then it is easy. In the code above I can simply move action_1 and action_2 anywhere I want:

class Foo

  # I have an attribute here: @actions

  def initialize(actions)
    @actions = actions
  end

  def do_something
    list = []
    @actions.action_1(list)
    @actions.action_2(list)
  end
end

# ----

class Actions
  def action_1(list)
    list[2] = "a"
  end

  def action_2(list)
    list[1] = "b"
  end
end

The action_1 and action_2 methods are no longer class level methods (no .self) because it is easier to code dependency injection with object level methods.

I do this even if my class has an actual “state”:

class Foo
  attr_accessor :last_element
  attr_accessor :list

  def do_iterate
    last_element = action_1(@last_element, @list)
  end

  private
  def self.action_1(last_element, list)
    list[2] = "a"
    last_element = 2
    return last_element
  end

end

Anyway, this is the first time a large refactoring is no longer a difficult and long process. Furthermore, there are no more surprises in my methods: I know what is going to happen just by looking at the method definition.


comments powered by Disqus