[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Refernce objects

Richard Turner

1/21/2005 2:23:00 PM

Hi,

I'm new to Ruby, so I'm still learning the Ruby Way. I'm also reading
Martin Fowler's 'Refactoring' at the moment and have realised that some
of the classes I've created in a program I'm writing fit his description
of value objects that should be refactored into reference objects.
Those classes are, in fact, wrappers over entities in a DB so I need a
factory (creation) method to always return the same object when given
the same creation parameter. E.g.:

Section.getSection(10) always returns the same object representing the
record in the DB with primary key '10'.

Since I can't make Section's constructor private in Ruby I wonder how
should I refactor my class into a reference object class? Indeed,
should I do this or does the Ruby Way use some other method?

Cheers,

Richard.



13 Answers

dblack

1/21/2005 2:36:00 PM

0

Richard Turner

1/21/2005 2:59:00 PM

0

On Fri, 2005-01-21 at 23:36 +0900, David A. Black wrote:
> >
> > I'm new to Ruby
>
> Welcome!
>

Thank you :)

>
> At the risk of over-simplifying, let me try to simplify :-)
>
> Generally in Ruby you don't need or see too many methods with 'get'
> and 'set' in their names, since there are a variety of ways to perform
> those operations less verbosely. For example, you can define a method
> called [] and then use index-style bracket syntax:
>
> class Section
> def self.[](n)
> # here, a fetch command for record n
> end
> end
>
> record = Section[10]
>
> That would bypass the need for a constructor (if that's appropriate in
> your case).
>

That's great, but isn't that just a more succinct way of doing the same
thing? I.e. isn't this the same?:

class Section
def self.loadedFromDB(sectionID)
# here, a fetch command for record sectionID
end
end

What I want []() or loadedFromDB() to return is a Section, but I don't
want people to be able to call Section.new in error, otherwise they'll
not get what they think. E.g.:

secFromFactory1 = Section.getSection(10)
=> #<Section:0xb7f8d468 @index=1, @firstQuestionID=515, @name="Living
Arrangements", @sectionID=10>
secFromFactory2 = SectionFactory.getSection(10)
=> #<Section:0xb7f8d468 @index=1, @firstQuestionID=515, @name="Living
Arrangements", @sectionID=10>

BUT:
secFromNew = Section.new(10)
=> #<Section:0xb7f728e8 @index=1, @firstQuestionID=515, @name="Living
Arrangements", @sectionID=10>

See the object IDs are the same from the factory but different using the
constructor. So if I do:

secFromFactory1.index = 2
secFromFactory2.index
=> 2
BUT:
secFromNew.index
=> 1

In short, I've rather painfully created a factory method that, given an
ID, returns the correct object from a pre-filled set. However, I now
need to stop people using Section.new and I can't. This leads me to
believe that there may be a Better Way, but I don't know what it is :(

Cheers,

Richard.




James Gray

1/21/2005 3:12:00 PM

0

On Jan 21, 2005, at 8:58 AM, Richard Turner wrote:

>> class Section
>> def self.[](n)
>> # here, a fetch command for record n
>> end
>> end
>>
>> record = Section[10]
>>
>> That would bypass the need for a constructor (if that's appropriate in
>> your case).
>>
>
> That's great, but isn't that just a more succinct way of doing the same
> thing? I.e. isn't this the same?:
>
> class Section
> def self.loadedFromDB(sectionID)
> # here, a fetch command for record sectionID
> end
> end

Yes, but less Javaish and more Rubyish. ;) We're not a wordy bunch
and [] is our standard accessor, say you convey the same information,
but type less.

> What I want []() or loadedFromDB() to return is a Section, but I don't
> want people to be able to call Section.new in error, otherwise they'll
> not get what they think. E.g.:

class Selection
private_class_method :new

def self.[]( id )
Section.new(
# whatever...
)
end
end

That help?

James Edward Gray II



Nospam

1/21/2005 3:17:00 PM

0

Richard Turner wrote:
> In short, I've rather painfully created a factory method that, given an
> ID, returns the correct object from a pre-filled set. However, I now
> need to stop people using Section.new and I can't. This leads me to
> believe that there may be a Better Way, but I don't know what it is :(

There are several things you can do (I think).

First you can simply make the initialize or self.new methods private.
Secondly you can override the self.new method to be the factory method
instead of [] or both. And third, there probably exists a Factory module
of some kind which does everything I just told you for you when you
simply include the module in your class.

Regards,

Peter

Richard Turner

1/21/2005 3:23:00 PM

0

On Sat, 2005-01-22 at 00:12 +0900, James Edward Gray II wrote:
> Yes, but less Javaish and more Rubyish. ;) We're not a wordy bunch
> and [] is our standard accessor, say you convey the same information,
> but type less.
>
Fair enough :) I'd shy away from it because to me (at least, at the
moment), it seems to imply enumeration. I'd be tempted to assume that
Section[] would 'fill' from index 0 so that, if there are any Sections,
there will certainly be Section[0]. Since the parameter is an ID, not
an index, it doesn't seem right.

On the other hand, Section{} sits fine in my head, since there's no
implied order there :)
>
> class Selection
> private_class_method :new
>
> def self.[]( id )
> Section.new(
> # whatever...
> )
> end
> end
>
> That help?
>
> James Edward Gray II
>

Perfect! Thanks. I'd just stumbled onto the idea of

class Section
class <<self
private :new
end
.
.
.
end

So I'm glad I'm starting to think in the right direction. I think I
have it now.

Thanks for your help folks!

Richard.



Florian Gross

1/21/2005 3:33:00 PM

0

Richard Turner wrote:

> Hi,

Moin.

> I'm new to Ruby, so I'm still learning the Ruby Way. I'm also reading
> Martin Fowler's 'Refactoring' at the moment and have realised that some
> of the classes I've created in a program I'm writing fit his description
> of value objects that should be refactored into reference objects.
> Those classes are, in fact, wrappers over entities in a DB so I need a
> factory (creation) method to always return the same object when given
> the same creation parameter. E.g.:
>
> Section.getSection(10) always returns the same object representing the
> record in the DB with primary key '10'.

I think this is also called the Multiton pattern.

Something like this ought to work: (I'm reusing .new here, I think it
would be a bigger surprise to have .new raise an Exception than it not
returning an unique object every time. You can however still provide
your own constructor and make it private fairly easily with class <<
self; private :new; end)

require 'thread'
class MyMultiton
# I'm not sure if this Mutex is really needed. The Hash class might
# already be using a critical section around allocator block calls
# anyway. Feedback on this is welcome.
@instance_cache_mutex = Mutex.new
@instance_cache = Hash.new do |hash, args|
result = self.allocate
result.send(:initialize, *args)
hash[args] = result
end

def self.new(*args)
@instance_cache_mutex.synchronize do
@instance_cache[args]
end
end

# Or whatever your initialize looks like...
def initialize(value)
@value = value
end
end

It can probably be done in a simpler way, but this ought to work.

James Gray

1/21/2005 3:34:00 PM

0

On Jan 21, 2005, at 9:23 AM, Richard Turner wrote:

> On Sat, 2005-01-22 at 00:12 +0900, James Edward Gray II wrote:
>> Yes, but less Javaish and more Rubyish. ;) We're not a wordy bunch
>> and [] is our standard accessor, say you convey the same information,
>> but type less.
>>
> Fair enough :) I'd shy away from it because to me (at least, at the
> moment), it seems to imply enumeration. I'd be tempted to assume that
> Section[] would 'fill' from index 0 so that, if there are any Sections,
> there will certainly be Section[0]. Since the parameter is an ID, not
> an index, it doesn't seem right.
>
> On the other hand, Section{} sits fine in my head, since there's no
> implied order there :)

We use [] for Arrays and Hashes:

names = { :james => "Gray" } # define a Hash
names[:james] # access a Hash entry

Hope that clears up the choice.

James Edward Gray II



Florian Gross

1/21/2005 3:44:00 PM

0

Richard Turner wrote:

> Fair enough :) I'd shy away from it because to me (at least, at the
> moment), it seems to imply enumeration. I'd be tempted to assume that
> Section[] would 'fill' from index 0 so that, if there are any Sections,
> there will certainly be Section[0]. Since the parameter is an ID, not
> an index, it doesn't seem right.
>
> On the other hand, Section{} sits fine in my head, since there's no
> implied order there :)

But we use the #[] method for both Hash, Array and Function access:

hash = { 1 => 2 }
hash[1]

ary = [1, 2]
ary[1]

fun = lambda { |x| x * 2 }
fun[2]

# mixture between lambda and hash. Nifty way for doing cached lambdas.
factorial = Hash.new do |hash, key|
hash[key] = case
when key == 1: 1
when key < 1: 0
else hash[key - 1] * key
end
end
factorial[500]

Richard Turner

1/21/2005 3:46:00 PM

0


> We use [] for Arrays and Hashes:
>
> names = { :james => "Gray" } # define a Hash
> names[:james] # access a Hash entry
>
> Hope that clears up the choice.
>

I think my brain's been melting today! I knew that, but somehow forgot
it until I tries to def Section.{}! :(

Thanks, my Section class is now much smaller and well factored. You
help is much appreciated!

Richard.



Anders Engström

1/21/2005 9:54:00 PM

0

On Sat, Jan 22, 2005 at 12:23:15AM +0900, Richard Turner wrote:
> On Sat, 2005-01-22 at 00:12 +0900, James Edward Gray II wrote:
> > Yes, but less Javaish and more Rubyish. ;) We're not a wordy bunch
> > and [] is our standard accessor, say you convey the same information,
> > but type less.
> >
> Fair enough :) I'd shy away from it because to me (at least, at the
> moment), it seems to imply enumeration. I'd be tempted to assume that
> Section[] would 'fill' from index 0 so that, if there are any Sections,
> there will certainly be Section[0]. Since the parameter is an ID, not
> an index, it doesn't seem right.
>

I agree :)

> On the other hand, Section{} sits fine in my head, since there's no
> implied order there :)
> >
> > class Selection
> > private_class_method :new
> >
> > def self.[]( id )
> > Section.new(
> > # whatever...
> > )
> > end
> > end
> >
> > That help?
> >
> > James Edward Gray II
> >
>
> Perfect! Thanks. I'd just stumbled onto the idea of
>
> class Section
> class <<self
> private :new
> end
> .
> .
> .
> end
>

Another option is to use the :new method on the class to implement the
class Section

class << self
# Called to get (or create) new instance
def new(id)
cached_instances()[id] || cached_instances()[id] = super
end

def cached_instances()
@cache = {} unless @cache
@cache
end
end


# Called on new instance
def initialize(id)
puts "Creating a new instance"
@id = id
end

end

puts Section.new(10).inspect
puts Section.new(11).inspect
puts Section.new(10).inspect
puts Section.new(11).inspect

Output:

Creating a new instance
#<Section:0xb7eeb6c4 @id=10>
Creating a new instance
#<Section:0xb7eeb64c @id=11>
#<Section:0xb7eeb6c4 @id=10>
#<Section:0xb7eeb64c @id=11>

This way you hide the factory pattern behind a "normal" constructor,
which might be nice (I wish java had something like this without
resorting to byte-code manipulation).

//Anders


--
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Anders Engström aengstrom@gnejs.net
http://www... PGP-Key: ED010E7F
[Your mind is like an umbrella. It doesn't work unless you open it.]