From: Nicholas Van Weerdenburg Date: 2004-12-13T16:12:04+09:00 Subject: Re: Abstracts and Interfaces in Ruby? On Mon, 13 Dec 2004 15:20:26 +0900, Miles Keaton wrote: > What's the recommended Ruby way to do abstract classes and abstract methods? > > Related : what's the Ruby way for interfaces? > > While Googling for it, I was thinking about what abstracts and > interfaces are used for in Java and PHP5 ... and it came down to > stopping with errors if a certain method was NOT implemented. > > So... is that kind of thing just not the Ruby Way? (To give you > errors for not doing something.) > > Would I just do workarounds to say: > > class AbstractSomething > def method > puts "error - abstract!" > end > end > > Or does Ruby have a whole different approach to this that I missed somewhere? > > Ruby has modules that can be mixed in to classes- a cleaner multiple inheritence that is not technically multiple inheritence. A good example is the Enumerable module- you mix it in to your classes to gain it's functionality. But interestingly, the most important method, each, isn't defined in the module and must be implemented in your class for Enumerable to work. I originally thought it would be nice to have a warning or some indication, but in Ruby there isn't concept of a contracts though that says "you must implement this"- aka interfaces or abstract base classes. One reason I suppose is that everything in Ruby is very dynamic, and it would be really hard to figure out pre-runtime wether or not a method had been implemented. For instance, it might be that a class uses method_missing to intercept the method call, and implement the method dynamically. Similarly, it's possible to undef methods. Thus it would be an awkward paradigm in Ruby- it is contrary to it's dynamic nature. But, while Ruby doesn't have the same concept, it's metaprogramming capabilities make it fairly easy to implement- though many would disagree with the goal behind that. And note that the error would be at run-time. For example, you can extend the Enumerable modules, and your changes apply to all objects in the system that use Enumerable: module Enumerable def each raise "you must implement each" end end So now you get a more specific error, and have a contract in place, albeit only at run-time. So you could do something like: module MyInteface def method1 raise "you must implement method1" end def method2 raise "you must implement method2" end end or extend Object to have a "abstract" keyword that does the boiler plate for you (the method definitions with the exception). Then your module looks like this: module MyInterface abstract :method1, :method2, :method3 end In either case, you then use it like this- class MyClass include MyInterface ... end While not complile type checking, it's a much more clear error then "undefined method". The first time code looks for the unimplemented method, there is a clear message. There are some cleverer things that could surely be done to find the error earlier- maybe when MyClass is defined, but they are a bit beyond my ruby experience. BTW- there is a library called "cs/interface", available as a ruby gem "csinterface". Search the newsgroup for that, and you'll find a lot of discussion. Many disagreed with the need, but the great thing about Ruby is it's flexibility. In general, unit testing is seen as a sufficient replacement for the benefits of typing. Once you have unit testing, you have such a focused evaluation of your codes contracts (implied) and behaviour that explicit support for types and interfaces is not worth the loss of dynamic behaviour. Regards, Nick -- Nicholas Van Weerdenburg