From: David Masover Date: 2009-11-30T04:18:43+09:00 Subject: Re: Class variables, instance variables, singleton; Ruby v. On Saturday 28 November 2009 02:42:08 pm Ralph Shnelvar wrote: > DM> It won't tell you how many Foo objects actually exist. It's more a > count of DM> how many have ever been created. > > Cool. > > Is there a way to tell how many instances actually exist? Is there > anything like a destructor for class instances? Just curious. These are separate questions. In MRI, you can tell how many instances exist by actually counting them. Look at the documentation for ObjectSpace. And yes, you can register a destructor -- that's also in ObjectSpace, and it's called a "finalizer". But remember, Ruby is garbage-collected -- that means you have absolutely no guarantee of _when_ that object will be removed. The finalizer is guaranteed to be called before the program exits, but that's the only guarantee you get. Both of these are relatively advanced topics, and something that shouldn't be needed in most programs. Keep in mind that in C++, then main purpose of a destructor is to free resources used by the object in question -- and most of these resources are simply other objects. So, the fact that Ruby is garbage- collected removes most of the reason for destructors/finalizers. An exception would be a resource that is more than just an object -- for example, an open file, or a database handle. But these are generally much scarcer resources than memory, so if at all possible, you want to manually close these as soon as you can, rather than waiting for something to get garbage collected. For example: open 'some_file' do |file| file.each_line do |line| # do some stuff with each line in the file end end What might not be obvious here is that the 'open' call will automatically close the file when the block exits. > So, basically, the reason that instance_variable_get is private to > Module is that one does not wish make breaking encapsulation too easy? Others have answered this already... In a very basic sense, yes, that seems like a valid answer. There is really no good reason why code outside a class should have to call instance_variable_get, most of the time. It's private so that when you try, you'll rethink your architecture so you don't need it. On the other hand, Ruby has open classes. While there are ways to supposedly lock it down and run untrusted code in a safe sandbox, in general, any code running in the same interpreter can do all sorts of things to existing classes. In other words, if you want to access the variable @bar in an object of class Foo, you could always do something like this: class Foo attr_reader :bar end There are many other ways of getting the same result. Now, Florian mentioned another aspect, which is that you still can only know anything about an object by calling methods on it. So even if instance_variable_get was public, there's nothing from stopping an especially paranoid class from redefining it or removing it. I think that's more than enough to answer your question, but if you're curious, you could always have a sort of "arms race" of trying to enforce encapsulation. For example, even if someone redefines instance_variable_get, I can always do this: Class.instance_method(:instance_method).bind(Foo).call(:instance_variable_get).bind(foo).call(:@bar) That means there's nothing the Foo class itself can do to hide the @bar variable from you, including redefining instance_method on itself. But wait! How do you know Class itself hasn't been modified? The lesson to learn here is that while Ruby is very good at encapsulation by convention, it's not very good at all at enforced encapsulation, as C++ is. At the end of the day, you have to remember that this is a language which lets you define null to be not null. Here, try this in irb: class NilClass def nil? false end end Now type any other command, and watch irb crash. > I mean, it seems easy enough to break encapsulation by adding an > accessor, right? Well, yes and no. Yes, you can break encapsulation by just adding an accessor to any instance variable that anyone was trying to hide from you. You can also break it by using one of several tricks to call the private instance_variable_get, and probably a few ways I haven't thought of. Ruby doesn't enforce encapsulation. On the other hand, accessors are the ultimate in encapsulation -- partly because they're the only sane way to get at instance variables, and partly because they're so easy to define. I remember wishing for them in Java, where good coding style led to lots of these: private int foo; public int getFoo() { return foo; } public void setFoo(int value) { foo = value; } I can see why Java people use tools like Eclipse -- there's no way you want to type all that stuff yourself. But it's still a good idea, because it allows you to change your internal structure to no longer require that variable, without changing your external interface. The point here is, if you actually expose an accessor, no one has to use instance_variable_get or anything like it -- they won't be mucking about with the internals of your class. (If they do, they're asking for trouble when you change something.) That means you can, among other things, override the behavior of the accessor on subclasses, replace it entirely with a method, etc. Just as an example: Suppose you have a class like this: class Line attr_reader :a, :b, :slope def initialize(point_a, point_b) @a = point_a @b = point_b @slope = (a.x - b.x) / (a.y - b.y) end end Now, maybe later on, you notice this isn't working out too well -- maybe not everyone cares about the slope, so you're wasting cycles calculating it every time. Maybe the points are changing frequently, and you don't want to have to re-calculate the slope on every change, only when someone requests it. Since you defined slope as a reader method, you can just change your implementation: class Line attr_reader :a, :b def slope (a.x - b.x) / (a.y - b.y) end end No one has to know that @slope no longer exists. Granted, you might have some coders who deliberately break things by talking to @slope directly, but if you've defined an accessor, they have to be a masochist to want to do it that way. Similarly, because it's just one command for you to define them, you really have no excuse not to. I suppose that's the fundamental difference -- in Java and C++, encapsulation can be enforced to some extent, while in Ruby, it can be easily circumvented. But Ruby makes it easy to write well-encapsulated code, while Java and C++ make it annoying and tedious.