Define method on objects

作者:   發佈於:  

This idea has been brewing in my head ever since I blogged about Mocking, and I finally decide to name it: Object::Method.

The purpose of Object::Method is to attach a method on objects but not classes, or at least fake it in the way that it appears to be so. This is native and trivial in Javascript:

// Given that "Stuff" is a function.
var o = new Stuff;
o.some_method = function() { ... };

This is also trivial in Ruby, with a bit meta-programming guts:

o = Stuff.new
class << o
    def some_method
        ...
    end
end

In a Classy OO language, methods are always defined on classes but not objects. In Ruby, ever single object has it's own meta-class, named after its' object_id. It lives as longs as the object lives, it dies as soon as the object dies. The most important property of all, one meta-class has only one corresponding object and vice versa. Methods defined in the body of the meta-class (opened with the class << o syntax) are available as instance methods on the given object.

Object::Method implements basically the same idea to make it possible to define methods on objects but not classes. Although, they are really defined on classes, only on a dynamically created class, just for the object. If you construct you objects in the following style, they can have their own methods easily:

package Stuff;

package Stuff001;
@ISA = qw(Stuff);
sub coffee { ... }

package Stuff002;
@ISA = qw(Stuff);
sub tea { ... }

package main;

my $o = Stuff001->new;
my $p = Stuff002->new;

The code above is sining: $o and $p are both Stuff. One has coffee and the other has tea.

If a specific class is created just for the object, in advance of the construction of that object, we can easily define methods in that class to make those methods only available for the object. Object::Method does just that and simplify the procedure. You don't need to define those Stuff001 Stuff002 classes, the module does it for you, automatically.

One very useful application is to do mocking in tests quickly:

use UNIVERSAL::Object::Method;

my $o = LWP::UserAgent->new;
$o->method("get", sub { pass "GET IS CALLED"; ... });

## Does not hit network.
$o->get("http://google.com");

Or to make a Prototypal OO, or Rubyish meta-programming system in Perl. Maybe those can be my (or your) yet-another holiday hacking project ;-)