G
Gavin Kistner
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_readerfurniture,: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_accessorname,: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.]
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_readerfurniture,: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_accessorname,: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.]