views:

68

answers:

1

Let's assume that we need to define a common class for trees (or some other objects we need to have in order to solve a problem). Since our class structure can be quite complex, I prefer to define class methods after its definition. Our common class BaseTree and one of our specific classes Tree are

class BaseTree
  class BaseNode; end
  class NodeA < BaseNode; end
end

class Container
  class Tree < BaseTree; end
end

After defining the class structure, we set #initialize for all nodes.

class BaseTree::BaseNode
  def initialize x
    p x
  end
end

If we test it, then everything is fine

Container::Tree::NodeA.new(1)
# => 1

However, if after that we add a method in the following way

class Container::Tree::NodeA
  def some_method; end
end

then it breaks the inheritance between NodeA and BaseNode !!

Container::Tree::NodeA.new(2)
# ~> -:30:in `initialize': wrong number of arguments(1 for 0) (ArgumentError)

In order to fix this, we have to define it explicitly

class Container
  class Tree < BaseTree
    class NodeA < BaseNode; end # explicit inheritance
  end
end

class Container::Tree::NodeA
  def some_method; end
end

or by the following way

class Container::Tree::NodeA < Container::Tree::BaseNode
  def some_method; end
end

class Container::Tree::NodeA < BaseTree::BaseNode
  def some_method; end
end

The last way needs to be used only once - the first time we add a method, and we can skip the parent class for later definitions

class Container::Tree::NodeA
  def another_method; end
end

After that it works fine, but I find it quite cumbersome, especially if there are a lot of tree types and many different nodes.

Is there a more elegant way to do such definitions?

+1  A: 

The way you should organize code in ruby is by using modules as namespace, and class inheritance for subclasses and inheriting behavior. I don't think ruby supports namespace inheritance (which is basically what you are doing by saying Tree inherits from BaseTree and referencing NodeA as Tree::NodeA), and hence this weird edge case where the bindings are not correct.

In any case, I don't think there's a valid scenario in which you need to organize the code the way you present it. The 'proper' way would be organizing it through modules defining the namespace, and classes defining behavior.

So, the way one would go by defining a tree as such is either simply declare the classes only, without namespaces, or with a namespace that differentiates them from classes that my have a name clash:

module UserInterface
  class Container; end

  class Tree; end

  class BaseTree < Tree; end

  class BaseNode; end

  class NodeA < BaseNode; end
end


module DataStructures
  class Tree; end

  class RedBlackTree < Tree; end
end 
Marcos Toledo