I’m still trying to understand these ideas, how they fit together and what consequences have each one. So this will be more of a presentation of those ideas and the theories I have than a post. The content will be too abstract, I’m sorry about that. I couldn’t find a good example to explain them.

While refactoring the GUI code of tic-tac-toe I found an interesting technique that simplify moving things around where they should be and may unveil smells. An example could be found in my tic-tac-toe.

It is based on the idea that objects are bags of functions (methods) that operate with data (attributes). Also, it has in mind that is desirable to have high-cohesion, which in simplified terms means that each one of the functions use all the data. If you have high-cohesion could mean that the data and functions belong together.

In object-oriented, we have different ways to send that data to the object. We could send those arguments in the methods:

class AllArguentsSentInEachMethod
  def method1(a, b, c)
    a + b + c
  end

  def method2(a, b, c)
    (a - b) * c
  end
end

We could send it as arguments in the constructor:

class AllArgumentsSentInTheConstructor
  attr_reader :a, :b, :c

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end

  def method1()
    a + b + c
  end

  def method2()
    (a - b) * c
  end
end

Or we could have a combination of both:

class SomeArgumentsSentInTheConstructor
  attr_reader :a, :b

  def initialize(a, b)
    @a = a
    @b = b
  end

  def method1(c)
    a + b + c
  end

  def method2(c)
    (a - b) * c
  end
end

You may prefer one or another depending on how the object collaborates with others.

If you know that the values of a, b and c do not change often, you may prefer to pass all the arguments in the constructor and all the users of the object not worrying about any of those values.

def solve_the_problem(object)
  object.method1()
  object.method2()
end

object = AllArgumentsSentInTheConstructor.new(1, 2, 3)
solve_the_problem(object)

#when the values change...
object = AllArgumentsSentInTheConstructor.new(9, 2, 3)
solve_the_problem(object)

If you know that some values change quite often, you may prefer to pass them as an argument and the clients are going to need to provide them.

def solve_the_problem(object)
  c = get_c()
  object.method1(c)
  object.method2(c)
end

object = SomeArgumentsSentInTheConstructor.new(1, 2)
solve_the_problem(object)

#when the values change...
solve_the_problem(object)

But this seems odd. We are contaminating the solve_the_problem procedure with more responsabilities: It is obtaining c. It is using c to complete what object needs. And it is using object to solve_the_problem.

We need to provide a, b and c before doing anything with object. The problem we have is that those values come from different places. Here the Builder pattern may work. But I didn’t know it before this problem and it may seem extra complicated, so I did a simpler version of it:

class IncompleteObject
  attr_accessor :a, :b, :c

  def method1()
    check_for_completion()
    a + b + c
  end

  def method2()
    check_for_completion()
    (a - b) * c
  end

  private
  def is_incomplete?()
    [a, b, c].any?{|attr| attr.nil?}
  end

  def check_for_completion()
    raise "Incomplete object" if is_incomplete?
  end
end

Having that, there’s no much difference from:

#in some part of the code:
object = AllArgumentsSentInTheConstructor.new(1, 2, 3)

#in another part of the code:
object.method1()
object.method2()

To:

#in some part of the code:
object = IncompleteObject.new()
object.a = 1
object.b = 2
object.c = 3

#in another part of the code:
object.method1()
object.method2()

And from:

#in some part of the code:
object = SomeArgumentsSentInTheConstructor.new(1, 2)

#in another part of the code:
c = get_c()
object.method1(c)
object.method2(c)

To:

#in some part of the code:
object = IncompleteObject.new()
object.a = 1
object.b = 2

#in another part of the code:
object.c = get_c()
object.method1()
object.method2()

With that, I had much more flexibility of moving object.a = ?, object.b = ? or object.c = ? around until I found the place that made more sense, and then changed those to constructor and method arguments again.