Converting from one class to another in Ruby

H

Harry Spier

Dear list members,

I have an application with a class Binary_matrix < Array (which is just
a two dimensional array filled with zeroes and ones). I also have other
classes which have to_Binary_matrix methods to convert objects of their
class to Binary_matrix objects.

The Ruby code I came up with to do the conversions is as follows.

class A
def to_Binary_matrix(from_initialize_method = false)
...some logic to convert class A object to binary_matrix_format
if from_initialize_method
return an_A_type_object_in_binary_matrix_format
else
return
Binary_matrix.new(an_A_type_object_in_binary_matrix_format)
end
end
end


class B
def to_Binary_matrix(from_initialize_method = false)
...some logic to convert class B object to binary_matrix_format
if from_initialize_method
return a_B_type_object_in_binary_matrix_format
else
return
Binary_matrix.new(a_B_type_object_in_binary_matrix_format)
end
end
end

class Binary_matrix < Array
def initialize arg
self.replace arg.to_Binary_matrix(true)
end
end

This allows me to do:

a = A.new
an_A_as_a_Binary_matrix_object = a.to_Binary_matrix
b_as_a_Binary_matrix_object = Binary_matrix.new(b)

It works but it seems a little clunky to me. Is their a more Rubyish or
elegent way of doing this. In particular is it possible to get rid of
the flag I use (from_initialize_method) to keep me from getting into an
infinite loop as I go between the .to_Binary_matrix methods and the
initialize method in Binary_matrix

Thanks in advance,
Harry
 
R

Robert Klemme

Dear list members,

I have an application with a class Binary_matrix < Array (which is just

The first thing I'd do differently is to cut off this inheritance.
Inheriting from core classes is rarely a good idea. In this case you
*use* an array for your storage of a binary matrix which is an
implementation detail that should be hidden inside the class. Also I
am sure you do not want all public Array methods to be available for
users of Binary_matrix. So delegation is a much better option here.
a two dimensional array filled with zeroes and ones). =A0I also have othe= r
classes which have to_Binary_matrix methods to convert objects of their
class to Binary_matrix objects.

The Ruby code I came up with to do the conversions is as follows.

class A
=A0def to_Binary_matrix(from_initialize_method =3D false)
=A0 =A0 ...some logic to convert class A object to binary_matrix_format
=A0 =A0 if from_initialize_method
=A0 =A0 =A0 =A0return an_A_type_object_in_binary_matrix_format
=A0 =A0else
=A0 =A0 =A0 =A0return
Binary_matrix.new(an_A_type_object_in_binary_matrix_format)
=A0 =A0end
=A0end
end

I find it hard to come up with good suggestions since a lot is missing
in this example (for example how you call it from #initialize). Why
don't you have a method
some_logic_to_convert_class_A_object_to binary_matrix_format which is
then called from #initialize and from to_Binary_matrix? That seems
the most natural solution and then you do not need the flag in the
first place.
class B
=A0def to_Binary_matrix(from_initialize_method =3D false)
=A0 =A0 ...some logic to convert class B object to binary_matrix_format
=A0 =A0 if from_initialize_method
=A0 =A0 =A0 =A0return a_B_type_object_in_binary_matrix_format
=A0 =A0else
=A0 =A0 =A0 =A0return
Binary_matrix.new(a_B_type_object_in_binary_matrix_format)
=A0 =A0end
=A0end
end

class Binary_matrix < Array
=A0 def initialize arg
=A0 =A0 =A0self.replace arg.to_Binary_matrix(true)
=A0 end
end

This allows me to do:

a =3D A.new
an_A_as_a_Binary_matrix_object =3D a.to_Binary_matrix
b_as_a_Binary_matrix_object =3D Binary_matrix.new(b)

It works but it seems a little clunky to me. =A0Is their a more Rubyish o= r
elegent way of doing this. =A0In particular is it possible to get rid of
the flag I use (from_initialize_method) to keep me from getting into an
infinite loop as I go between the .to_Binary_matrix methods and the
initialize method in Binary_matrix

It looks absolutely clunky.

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
H

Harry Spier

Dear Robert,

Many thanks for your reply.
I have simplified somewhat. Its an OCR application where Binary_matrix
is a black and white pixel representation of a page of text(or line of
text, or letter or part of a letter etc.). There are many different
objects which are to be converted into Binary_matrix such as the above
portions of an existing Binary_matrix object, or Rmagick ImageList
objects, or graphic objects processed by other packages than RMagick.
This list will definitely grow in the future so for that reason I wanted
the conversion to Binary_matrix logic to reside in the class being
converted to Binary_matrix not in the Binary_matrix class.

I.e. I didn't want a list of statements in Binary_matrix initialize
method like:
if arg.class == ImageList then ImageList_conversion end
if arg.class == JPEG then jpeg_conversion end
etc. etc.
Rather I wanted my Binary_matrix object to check if the object to be
converted to a Binary_matrix had a to_Binary_matrix method and then to
call it.

I also require all the Array methods for Binary_matrix because of the
complex traversing and manipulation of the pixel data. To give a
trivial example: does a black pixel in line n touch a black pixel in
line n+1? What are the array ranges of a contiguous group of black
pixels etc. Change a contiguous range of pixels to white (i.e. clear up
noise) etc.

Again because of complexity of the application I wanted to make the code
as humanly readable as possible. So I want to be able to say
page_in_pixels = page_image.to_Binary_matrix
as well as
page_in_pixels = Binary_matrix(page_image)
(I know that may sound trivial but when you go back to look at your code
a few months later, everything that makes it more easily readable
helps.)
So for those reasons the solution I came up with is as follows:

(I'm not saying its necessarily a good solution and I really wish I
could make it less clunky. if there was some way to tell whether method
to_Binary_matrix was being called within the context of Binary_matrix
initialize method or outside it then the flag -from_initialize_method-
wouldn't be necessary)

class A
def to_Binary_matrix(from_initialize_method = false)
if from_initialize_method
return self
else
return Binary_matrix.new(self)
end
end
...
end

class Binary_matrix < Array
def initialize(arg)
called_from_initialize =true
if arg.respond_to? :to_Binary_matrix
arg = arg.to_Binary_matrix (called_from_initialize)
else
....do some error reporting
end
validate arg #Is the Binary_matrix object correctly formed?
end
...
end
 
H

Harry Spier

Correction to post (finger trouble)
Harry said:
class A
def to_Binary_matrix(from_initialize_method = false)
if from_initialize_method
...logic to convert to Binary_matrix format
and store result in two_dimensional_binary_array...
 
R

Robert Klemme

Many thanks for your reply.

You're welcome.
I have simplified somewhat. =A0Its an OCR application where Binary_matrix
is a black and white pixel representation of a page of text(or line of
text, or letter or part of a letter etc.). =A0There are many different
objects which are to be converted into Binary_matrix such as the above
portions of an existing Binary_matrix object, or Rmagick ImageList
objects, or graphic objects processed by other packages than RMagick.
This list will definitely grow in the future so for that reason I wanted
the conversion to Binary_matrix logic to reside in the class being
converted to Binary_matrix not in the Binary_matrix class.

That's perfectly OK. Just the way you coded your logic is weird IMHO.
Just do this for your classes:

class A
def initialize( what goes here? )
# what do you want to do here?
end

def to_binary_matrix
bm =3D BinaryMatrix.new
# fill bm
bm
end
end
I.e. I didn't want a list of statements in Binary_matrix initialize
method like:
if arg.class =3D=3D ImageList then ImageList_conversion end
if arg.class =3D=3D JPEG then jpeg_conversion end
etc. etc.

I would not want it that way either nor did I suggest this.
Rather I wanted my Binary_matrix object to check if the object to be
converted to a Binary_matrix had a to_Binary_matrix method and then to
call it.

Just call it (see below).
I also require all the Array methods for Binary_matrix because of the
complex traversing and manipulation of the pixel data. =A0To give a
trivial example: does a black pixel in line n touch a black pixel in
line n+1? What are the array ranges of a contiguous group of black
pixels etc. Change a contiguous range of pixels to white (i.e. clear up
noise) etc.

These are the exact operations that I would place in a class
BinaryMatrix (btw. note the naming conventions for Ruby constants).
An Array is just not a matrix. By exposing all Array methods client
code (i.e. classes that use BinaryMatrix) needs to know about the
internal representation of a BinaryMatrix which is about the worst
thing you can do in an OO application from a design perspective.
Again because of complexity of the application I wanted to make the code
as humanly readable as possible. =A0So I want to be able to say
page_in_pixels =3D page_image.to_Binary_matrix
as well as
page_in_pixels =3D Binary_matrix(page_image)

For that to work you only need

def BinaryMatrix(x)
x.to_binary_matrix
end

and implement #to_binary_matrix in all your classes properly.
(I'm not saying its necessarily a good solution and I really wish I
could make it less clunky. if there was some way to tell whether method
to_Binary_matrix was being called within the context of Binary_matrix
initialize method or outside it then the flag -from_initialize_method-
wouldn't be necessary)

Why and what do you want to convert to BM in #initialize? This hasn't
become clear yet.

Cheers

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
H

Harry Spier

Robert said:
Just do this for your classes:

class A
def initialize( what goes here? )
# what do you want to do here?
end

def to_binary_matrix
bm = BinaryMatrix.new
# fill bm
bm
end
end



Just call it (see below).
Why and what do you want to convert to BM in #initialize? This hasn't
become clear yet.

I wrote
I meant to write: (more finger trouble :) )
I want to be able to do both:
page_in_pixels = page_image.to_Binary_matrix
page_in_pixels = Binary_matrix.new(page_image)
(i.e. Binary_matrix.new doesn't create an empty Binary_matrix object but
takes as a parameter an object which is converted to a Binary_matrix )

so for that reason my solution had as follows:
1) obj_a.to_Binary_matrix calls Binary_matrix.new to create a new
Binary_matrix object.
I.e.In this case #to_Binary_matrix is being called outside the context
of Binary_matrix#initialize

but

2)Binary_matrix#initialize contains an arg = arg.to_Binary_matrix
statement to access the conversion logic.
I.e. #to_Binary_matrix is being called within the context of
Binary_matrix#initialize

so my solution (retyped at the end of this post had a flag to keep from
getting in an infinite loop and to do the following:

(I'm not sure if I'm missing something really obvious but I still don't
see that I don't need a flag or someway to tell if #to_Binary_matrix is
being called within Binary_matrix#initialize or not)

case 1:
p = page_image.to_Binary_matrix
calls page_image.to_Binary_matrix
--> calls Binary_matrix.new(page_image) since flag_not_set
Binary_matrix.initialize(arg)
arg = arg.to_Binary_matrix(flag_set)
-->calls arg.to_Binary_matrix(flag_set)
convert page_image to binary format since flag_set
Binary_matrix <-- return binary matrix format since flag_set
<-----Binary_matrix.new returns Binary_matrix obj

case 2:
p = Binary_matrix.new(page_image)
calls Binary_matrix.new(page_image)
--> Binary_matrix.initialize(arg)
arg = arg.to_Binary_matrix(flag_set)
-->calls arg.to_Binary_matrix(flag_set)
-->calls arg.to_Binary_matrix(flag_set)
convert page_image to Binary_matrix format since
flag_set
Binary_matrix <-- return binary matrix format
<-----Binary_matrix.new returns Binary_matrix obj




class Binary_matrix < Array
def initialize(arg)
called_from_initialize =true
if arg.respond_to? :to_Binary_matrix
arg = arg.to_Binary_matrix (called_from_initialize)
else
....do some error reporting
end
validate arg #Is the Binary_matrix object correctly formed?
self.replace arg
end
...
end


class A
def to_Binary_matrix(from_initialize_method = false)
if from_initialize_method
...logic to convert to Binary_matrix format
and store result in two_dimensional_binary_array...
return two_dimensional_binary_array
else
return Binary_matrix.new(self)
end
end
...
end
 
R

Robert Klemme

I wrote

I meant to write: (more finger trouble :) )

Hopefully the trouble does not extend further up... :)
I want to be able to do both:
page_in_pixels = page_image.to_Binary_matrix
page_in_pixels = Binary_matrix.new(page_image)
(i.e. Binary_matrix.new doesn't create an empty Binary_matrix object but
takes as a parameter an object which is converted to a Binary_matrix )

This seems quite backwards to me. What do you gain by this other than
making the code more complicated than necessary? You say it feels
awkward and still seem to insist on this approach.
so for that reason my solution had as follows:
1) obj_a.to_Binary_matrix calls Binary_matrix.new to create a new
Binary_matrix object.
I.e.In this case #to_Binary_matrix is being called outside the context
of Binary_matrix#initialize

but

2)Binary_matrix#initialize contains an arg = arg.to_Binary_matrix
statement to access the conversion logic.
I.e. #to_Binary_matrix is being called within the context of
Binary_matrix#initialize

You can have it easier

class A
def to_binary_matrix
bm = BinaryMatrix.new
fill bm
bm
end

def fill bm
# do whatever it takes to make state of self
# appear in bm
end
end

class BinaryMatrix
def initialize(x = nil)
x.fill self rescue nil
end
end

IIRC this is similar although not identical to what I have suggested in
my first posting. But frankly, I find this only slightly less awkward.
In my experience providing too many ways to do things is rather
causing trouble than improving things. Plus, #to_x is a common idiom in
Ruby to provide conversions of something into something else - in fact
it is quite ubiquitous).
so my solution (retyped at the end of this post had a flag to keep from
getting in an infinite loop and to do the following:

(I'm not sure if I'm missing something really obvious but I still don't
see that I don't need a flag or someway to tell if #to_Binary_matrix is
being called within Binary_matrix#initialize or not)

See above.

Cheers

robert
 

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,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top