Methods depend on pairs of objects - best parctise pls?

O

Oren Shani

Hi All,

First of all I think you should know that I am quite new to Ruby. I have
many years of experience with other programming languages tough, and I
must say that Ruby is really great!

And now to my question - I am working on something that makes use of
proximity functions, which are functions that take two objects ("base"
and "reference") and return a real number in the range of [0..1], based
on how "the same" the objects are (1 means identical, 0 means completley
different and a fractions means a certain degree of proximity). The
objects do not have to be of the same type. For example, a number can be
compared to a range, a string can be compared to a regular expression,
etc, but of course, not all combinations are possible.

Now the point is that the process for calculating the proximity very
much depends on the types of objects compared, so it is actually a
method of the objects-pair, so the most obvious thing I can do is define
a class for each valid pair combination, but that does not seem to me as
very elegant. Another thing I can do is to define the proximity function
as a method of each object and somehow re-define the needed sub-methods
per each class of valid reference-object, but that still looks a little
messy, I think.

So does anyone have any other ideas for doing this kind of object-pairs
dependent coding, is ther some kind of best-practice for this situation?

Your thoughts and ideas are much appreciated!

Oren
 
M

Max Williams

I would do a module and then mix that in to any of your required
classes. The module has one public method, 'proximity' that looks at
the class of 'self' and of the other object, and then decides what to do
accordingly. The various behaviours for comparing different classes can
be expressed through various private methods. eg

#save in a file called proximity.rb
module Proximity
def proximity(other)
case [self.class, other.class]
when [Array, Array]
return array_array_proximity(self, other)
when [Array, String] || [String, Array]
return string_array_proximity
#etc
else
return false #or raise
end
end
end

private
def array_array_proximity(arg1, arg2)
#your logic here
end

def string_array_proximity(arg1, arg2)
#your logic here
end

#etc
end

Now you can include this module into the various classes - somewhere in
your initialisation process you can do

require 'proximity'

class Array
include Proximity
end

class String
include Proximity
end

etc
 
M

Max Williams

Max said:
oops this should have been
return string_array_proximity(self, other)

You get the idea anyway.

oh and in case you don't have a string comparison method yet there's a
nice gem called amatch which implements many different string proximity
algorithms, eg levenshtein distance etc.
 
K

Kaspar Schiess

So does anyone have any other ideas for doing this kind of object-pairs
dependent coding, is ther some kind of best-practice for this situation?

If there is one proximity function per valid pair, storing that
function/method in a specialized class is not a bad idea. Most likely
though you'd have some kind of default behavior for the pair proximity
and then some special cases depending on one, the other or both of the
elements of the pair.

I guess a good class design would try to capture the common cases first,
specializing only where needed. In the general case you might have
something like

class ProximityTuple
attr_reader :base, :reference

# setup ..

def proximity
if base.respond_to? :proximity_to
return base.proximity_to(reference)
end

if reference.respond_to? :proximity_to
return reference.proximity_to(base)
end

# default behaviour
end
end

class SpecializedTuple < ProximityTuple
# ...

def proximity
# special implementation
end
end


This would be customizable by either letting the default do the work, or
let one of the participants do the work, or the specialized tuple.

If you would care to more closely describe the kind of objects you're
handling and the function(s) involved this might get simplified (again)
considerably. This is the best I can come up with in the abstract case ;)

regards,
kaspar
 
O

Oren Shani

Max said:
oh and in case you don't have a string comparison method yet there's a
nice gem called amatch which implements many different string proximity
algorithms, eg levenshtein distance etc.

Max,

Many thanks for your replies.

I like your approach because it makes it easy to metacode the proximity
module, that is to allow users to define metaobjects by something like:

class txtwords < Factfile
basetype Array

porximate_to Array,Array_Array_proxy
proximate_to String,Array_String proxy

...


So every call to the proximate_to function will actually re-program the
case part of the module (would appreciate any ideas about how to best
implement that...).

Also, many thanks for the reference to the amatch module. I am quite a
lazy person so I would be happy to re-use anything that already exists
:)

Best,

Oren
 
O

Oren Shani

Kaspar said:
If there is one proximity function per valid pair, storing that
function/method in a specialized class is not a bad idea. Most likely
though you'd have some kind of default behavior for the pair proximity
and then some special cases depending on one, the other or both of the
elements of the pair.

I guess a good class design would try to capture the common cases first,
specializing only where needed. In the general case you might have
something like

class ProximityTuple
attr_reader :base, :reference

# setup ..

def proximity
if base.respond_to? :proximity_to
return base.proximity_to(reference)
end

if reference.respond_to? :proximity_to
return reference.proximity_to(base)
end

# default behaviour
end
end

class SpecializedTuple < ProximityTuple
# ...

def proximity
# special implementation
end
end


This would be customizable by either letting the default do the work, or
let one of the participants do the work, or the specialized tuple.

If you would care to more closely describe the kind of objects you're
handling and the function(s) involved this might get simplified (again)
considerably. This is the best I can come up with in the abstract case
;)

regards,
kaspar

Kaspar,

I would be happy to share more details about what I am doing with you
and with the community but it is a little to early for that, not because
I have something to hide, but because I don't have much to show. So
right now I am more interested in general approcahes and best practices,
that will help me decide about some conceptual issues. In this context
your answer is very helpful.

Thanks,

Oren
 

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

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top