Object Creation In Ruby
Please bear with me as I stumble through 'Programming Ruby'. Today's topic is object creation.
Here's a very simple Ruby class. Its three attributes are all initialized in the constructor, aptly named initialize(). Each attribute has a public accessor and mutator granted by the attr_reader
and attr_writer
keywords, respectively. It's worth pointing out here that methods are public by default, though access can be tightly controlled as I'll show later.
class Song
attr_reader :name, :artist, :duration
attr_writer :name, :artist, :duration
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
end
def to_s # instance method
"#{@artist} - #{@name} (#{@duration})"
end
def self.clone(s) # class method
newSong = Song.new(s.name, s.artist, s.duration)
return newSong
end
end
Creating an instance of this class is simple:
s1 = Song.new("All Things To All Men", "Cinematic Orchestra", 290)
However, the following invocation results in a
"private method `initialize' called for #Song:0x100f3f88 (NoMethodError)"
error:
s2 = s1.initialize("Seeing Red", "Minor Threat", 125)
There are a couple of interesting things to note here. First off, even though we didn't scope
initialize()
as private, apparently it is anyway. More interesting, though, is that we instantiate objects using the class method new()
provided in the base class Object
and not through the use of a global operator as in languages like C++. While initialize()
plays an important role in object creation, it doesn't tell the whole story. The class method Song.new()
allocates memory for a new, uninitialized object. It then invokes the new object's initialize()
method, passing along the arguments from the original call to new()
. In contrast to languages like Java that treat memory allocation and initialization of objects atomically, Ruby allows the programmer to handle these operations discretely by overriding Class.new()
. It might help to think of new()
as a built-in factory method inherited from the base class. Here's a pared down version of our Song class that illustrates how you might use this strategy to limit object creation by performing lookups against a cache.
class Song
attr_reader :name, :artist, :duration
def initialize name, artist, duration
@name = name
@artist = artist
@duration = duration
end
def self.new *args
# check cache first
obj = CacheManager.lookup args[0]
# if cache miss, then create new song
if obj == nil
newObj = self.allocate
newObj.send :initialize, *args
return newObj
end
# we have a hit, just return it
obj
end
end
Minus the cache bit this example was basically lifted straight out of Programming Ruby. Instead of blindly creating an object every time
Song.new()
gets called, we first check the cache to see if an object with the same key already exists. If so we simply return the cache hit, otherwise we reimplement Object.new()
, allocating memory for a generic object, then invoking Song#initialize()
by passing its symbol to Song#send()
(once again inherited from base class Object
).References
Thomas, D. and Hunt, A. "Programming in Ruby." Dr. Dobb's Journal. 2001. http://www.ddj.com/184404436
"Ruby 1.8.4 Documentation." Ruby-Doc.org. 2006. http://www.ruby-doc.org/core/
Thomas, Dave. Programming Ruby. 2005. The Pragmatic Bookshelf.
1 comments:
Hi, I get the following error:
/Users/id/tmp/yamldoc/lib/class_attributes.rb:115:in `new': uninitialized constant CacheManager (NameError)
There isn't any reference to "Cache Manager" or "CacheManager" in the ruby documentation. So where CacheManager is? Unofficial / private class?
Post a Comment