Avoiding if requires type mutation -- evil.rb?

M

Mikael Brockman

I am writing a tree editor. The algorithm for displaying a node is
recursive. On leaf nodes, it's trivial. Branches need to display all
their kids.

I would like to implement this with three classes: the Leaf, the
Branch, and the Node. Leaf & Branch are subclasses of Node.

The problem: nodes are mutable. You can add a kid to a node. If that
node's a Leaf, it needs to become a Branch.

I can think of three solutions.

- Add a wrapper object around every Node, and give it a #replace
method.

- Use Object#become from evil.rb.

- Represent nodes and leaves as instances of the same class, and use if
statements.

I'd like #2 the best if evil.pb wasn't likely to win awards for being
the nastiest hack of all time. (Props to the authors!) I don't like #1
or #3, but if I had to choose, I'd go with #3.

Making nodes non-mutable would be annoying and unintuitive, I think.

What would you do?
 
D

David A. Black

Hi --

I am writing a tree editor. The algorithm for displaying a node is
recursive. On leaf nodes, it's trivial. Branches need to display all
their kids.

I would like to implement this with three classes: the Leaf, the
Branch, and the Node. Leaf & Branch are subclasses of Node.

The problem: nodes are mutable. You can add a kid to a node. If that
node's a Leaf, it needs to become a Branch.

I can think of three solutions.

- Add a wrapper object around every Node, and give it a #replace
method.

- Use Object#become from evil.rb.

- Represent nodes and leaves as instances of the same class, and use if
statements.

I'd like #2 the best if evil.pb wasn't likely to win awards for being
the nastiest hack of all time. (Props to the authors!) I don't like #1
or #3, but if I had to choose, I'd go with #3.

Making nodes non-mutable would be annoying and unintuitive, I think.

What would you do?

One possibility would be have a Branchlike module and extend leaf
objects with it when they became branches:

module Branchlike
def show_kids
...
end
# etc.
end

class Branch
include Branchlike
# etc.
end

class Leaf
def add_node
extend(Branchlike)
end
# etc.

or something along those lines, maybe even more modularized.


David
 
M

Mikael Brockman

David A. Black said:
Hi --



One possibility would be have a Branchlike module and extend leaf
objects with it when they became branches:

module Branchlike
def show_kids
...
end
# etc.
end

class Branch
include Branchlike
# etc.
end

class Leaf
def add_node
extend(Branchlike)
end
# etc.

or something along those lines, maybe even more modularized.

Hm. Yes, that could work. The only issue I can see with it regards
removing kids: I'd have to keep extending Branchlike and Leaflike, which
would make leaf.is_a? Branchlike true. I'll probably never use #is_a?,
though.

The proxy thing is kinda starting to grow on me though. It's so simple.
Node#become would be easy to make. I think I prefer that.
 
R

Robert Klemme

Mikael Brockman said:
I am writing a tree editor. The algorithm for displaying a node is
recursive. On leaf nodes, it's trivial. Branches need to display all
their kids.

I would like to implement this with three classes: the Leaf, the
Branch, and the Node. Leaf & Branch are subclasses of Node.

The problem: nodes are mutable. You can add a kid to a node. If that
node's a Leaf, it needs to become a Branch.

I can think of three solutions.

- Add a wrapper object around every Node, and give it a #replace
method.

- Use Object#become from evil.rb.

- Represent nodes and leaves as instances of the same class, and use if
statements.

I'd like #2 the best if evil.pb wasn't likely to win awards for being
the nastiest hack of all time. (Props to the authors!) I don't like #1
or #3, but if I had to choose, I'd go with #3.

Making nodes non-mutable would be annoying and unintuitive, I think.

What would you do?

I would not necessarily change the type of the instance. Instead I had just
class Node and would use strategy / state pattern. Using extend with a
Module (like David suggested) is one way to accomplish this, but it becomes
complitcated if the Branch should become a Leaf again. You could instead
keep an instance around that implements all behavior that depends on the
state 'leaf' vs. 'branch'.

Example:


require 'singleton'

class Node
class Leaf
include Singleton

def leaf?() true end
def each(owner) end
end

class Branch
include Singleton

def leaf?() false end
def each(owner,&b) owner.children.each(&b) end
end

attr_reader :children

def initialize
@children = []
@state = Leaf.instance
end

def leaf?() @state.leaf? end
def branch?() ! leaf? end

def each(&b) @state.each(self,&b) end

def add(node)
@children << node
@state = Branch.instance
end

def remove(node)
@children.delete node
@state = Leaf.instance if @children.empty?
end
end


Kind regards

robert
 
A

Assaph Mehr

Why make the distinction between leaf and node? A leaf is just a node
without children.

Why not simply have something like (code written to email, not
checked):

class Node
attr_accessor :children

def initialize
children = []
end

def leaf? children.size.zero? end
end

And use it as

if node.leaf?
# do leafy stuff
else
# do branchy stuff
end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,164
Messages
2,570,901
Members
47,439
Latest member
elif2sghost

Latest Threads

Top