From: "Ara.T.Howard" Date: 2004-01-30T03:04:55+09:00 Subject: Re: Read-Only Array Access On Thu, 29 Jan 2004, Gavin Kistner wrote: > Date: Thu, 29 Jan 2004 17:11:40 GMT > From: Gavin Kistner > Newsgroups: comp.lang.ruby > Subject: Read-Only Array Access > > While I understand the (very valid) reasons that my RCR [see: > http://rcrchive.net/rcr/RCR/RCR201] got shot down, an underlying problem > still remains for me. > > I want to provide access to a full collection of elements (returned as > an array, or iterated through an arbitrary .each{} block), but not let > the objects be modified. > > Is this possible? > > For example, assume that a House has Furniture and ArtObjects inside it. > I need to be able to modify those collections independently; at any time > give external objects access to read the full collection; and (the hard > part) prevent the objects in the collections from being changed. > > > class House > attr_reader(:furniture,:jewelry,:furnitureValue,:jewelryValue) > def initialize > @furniture,@jewelry=[],[] > @furnitureValue,@jewelryValue=0,0 > end > def addFurniture (*newPieces) > @furniture.push(*newPieces) > newPieces.each{ |p| @furnitureValue+=p.value } > end > def addJewelry (*newPieces) > @jewelry.push(*newPieces) > newPieces.each{ |p| @jewelryValue+=p.value } > end > end > class Obj > attr_accessor(:name,:value) > def initialize(name,value) > @name,@value=name,value > end > end > > myHouse = House.new > myHouse.addFurniture( Obj.new('Chair',100), Obj.new('Table',200) ) > myHouse.furniture.each{ |p| puts p.name } #fine > myHouse.furniture.each{ |p| p.value*=2 } > #uh-oh! myHouse.furnitureValue is now out of sync > > > > The problem cannot be solved by freezing @furniture, since I want to be > able to modify it at any time. The problem could be sort of solved by... > > def furniture > return @furniture.dup > end > > ...except that (a) this provides 'silent' protection (the code can > modify the values, it just doesn't do what was expected) and (b) the > returned array is no longer synced with the original: > > foo = myHouse.furniture > myHouse.addFurniture( Obj.new('Credenza',1000) ) > # foo has two items, the house really has three. > > > > So...how do I solve this? The problem is that a 'read-only' copy of an > array doesn't prevent code from getting a read-only reference to an > element in that array, and then modifying it directly. > > > [And as an aside...I realize that the particular problem could be solved > if I either disallowed .value= or recalculated the furnitureValue by > iterating the array each time it was asked for. Or I could have the > furniture pieces know what house they are in, and any time they are > changed have them tell the house that they were modified and any > dependent information needs to be recalculated. While all are solutions > to the above, I can sense that I'm going to be repeatedly bumping up > against this problem...the desire to keep a collection of elements who > should be able to be read, but not directly modified.] first - why not freeze the objects in the array? or simply give an access method which does something like class House def each_furniture_peice @furniture.each{|f| f = f.dup; f.freeze; yield f} end end and make this the _only_ method (and perhaps a few other) of getting at the furniture. or you could use interfaces: class FurniturePrivate def intialize peice; @peice = peice; end def method_missing(meth, *args, &block); peice.send meth, *args, █ end end class FurniturePublic def intialize peice; @peice = peice; end def exposed_method(*args); @peice.exposed_method *args; end end class House private class Furniture ... end def initialize @furniture = [ Furniture.new, Furniture.new ] @public_furniture = @furniture.map{|f| FurniturePublic.new f} @private_furniture = @furniture.map{|f| FurniturePrivate.new f} end def _furniture @private_furniture end public def furniture @public_furniture end end there a million ways (all better probably too) to do the above - even generically. the basic idea is that you either need to limit access somehow or give two entirely different access methods (interfaces) other approaches could include a 'lock and key' approach whereby a peice refused to modify itself unless 'unlocked' by a key know only to the house... 2 cts. -a -- ATTN: please update your address books with address below! =============================================================================== | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov | PHONE :: 303.497.6469 | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328 | STP :: http://www.ngdc.noaa.gov/stp/ | NGDC :: http://www.ngdc.noaa.gov/ | NESDIS :: http://www.nesdis.noaa.gov/ | NOAA :: http://www.noaa.gov/ | US DOC :: http://www.commerce.gov/ | | The difference between art and science is that science is what we | understand well enough to explain to a computer. | Art is everything else. | -- Donald Knuth, "Discover" | | /bin/sh -c 'for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done' ===============================================================================