On 10/1/06, David V. [email protected] wrote:
(generating public wrappers for private methods of tested classes) would
they are necessary seems just overkill.
It almost sounds that you’re using Ruby as only a “better Java”
Many people consider Smalltalk-style OO and metaprogramming
in Ruby extremely important.
Back to the point. It is very unusual for OO system to have
public/private distinction.
(http://www.approximity.com/ruby/Comparison_rb_st_m_java.html)
The OO language Smalltalk did not have it, and other OO systems
like Objective C, CLOS, Perl 5 and Python do not have it either.
The only listed languages that do are C++, Java and Ruby.
Conceptual model behind C++ is very far from Smalltalk-style OO.
In fact it has very little to do with OO, C++ objects are little
more than abstract data types. There is nothing remotely similar
to OO “message passing”, the “methods” are merely nicely
namespaced functions with some hacks for “inheritance” (and you
need to turn inheritance support explicitly using virtual keyword,
by default you get nothing more than nicely namespaced functions).
There is no such thing as interface of an objects. The same object
is going to behave in completely different way depending on
context - a single method is going to do something completely different
depending on whether you call it from object’s class, subclass of it
(and there is public and private inheritance, that changes object’s
interface), or a different place.
The same method can behave differently depending on type of variable
to which object is assigned - if Foo is a subclass of Bar, then the
following code:
Foo *f = new Foo();
Bar *b = f;
b->bar();
f->bar(); // bar redefined in Foo, bar not virtual
can do two completely different things.
The model is already so complicated, that adding private/public
distinction to everything doesn’t affect it much.
Java object model is halfway between C++ and Smalltalk.
Objects still do not have single clear interfaces. If Foo is
a subclass of Bar, and both have a private method bar(),
then calls to some_foo_obj->bar() will behave differently
depending on whether the call was done from methods declared in
Foo class or from methods declared in Bar class.
Private methods are also automatically final, and therefore
they cannot be overriden. So private methods in Java still
have more to do with global functions than with regular methods.
Fortunately they at least got rid of private inheritance.
I’m not saying “private methods” are useless in C++/Java.
They are simply not methods at all. They are nicely encapsulated
global helper functions for implementation of a class. They have
class-specific namespace and cannot be overriden, so a class
can define whatever private functions it wants to, and they do not
affect anything else.
The only language left that has Smalltalk-style OO and this kind of
access controls in Ruby. Instead of objects having single interface
like Smalltalk or multiple interfaces like C++/Java, Ruby objects has
exactly two - one for normal method calls, and a second for “implicit
self”
method calls. And we don’t get much - defining method “private”
doesn’t provide much “protection” as there is a single namespace
for all methods, so it can be accidentally redefined in a subclass.
On the other hand methods cannot call private methods of different
object of the same class (or its subclasses), so objects with
less-defined
interface and hidden implementation need to either hack around
access control inside own methods (ugly), or to have their internals
public.
A very simple and very common example would be == method
that checks if both objects have the same class, and if so,
whether their fields are identical.
While writing this reply I found something that really surprised me.
It is possible to get real private variables in Ruby, with private
per-class namespaces, not overridable, and working with other objects
of the same class, just like in C++/Java, by a few lines of
metaprogramming.
They are much better at encapsulation control than the official classes.
Here’s a snippet:
class Class
def define_private_method(name, &blk)
define_method(:xyzzy, &blk)
class_variable_set(“@@#{name}”.to_sym, instance_method(:xyzzy))
remove_method :xyzzy
end
end
class Foo
define_private_method(:bar) { puts “Hello” }
# Not the prettiest syntax possible.
# A slightly different idea:
# @@bar.call(self, *arguments)
def foo; @@bar.bind(self).call() end
end
Foo.new.foo
By movinc @@bar around or keeping it in some other variable (like
file-local, or package-local) we can easily implement advanced
visibility
control.
So I think it would be good idea to have method visibility controls
removed because:
- They do not fit Smalltalk-style OO, so removing them would make
everything simpler - We get very limited benefit from them due to lack of C++/Java-style
features like per-class namespaces for private methods (or even
instance variables), and ability to call private methods of other
objects of the same type - They make metaprogramming, unit testing etc. more difficult
- They provide very limited control over visibility
- Very simple Ruby metaprogramming gives us much more powerful private
variables anyway. - I think it’s unlikely for current visibility control system to lead
to any cool things. As far as I can tell it was never used for
implementing any magic. There is no obvious way to add any features
(even to get them to the level of that metaprogramming snippet)
without greatly complicating the language. - Ruby 2 is exactly the right time for doing such changes