http://qs321.pair.com?node_id=219728

The background

This meditation directly follows the discussion around the OOP tutorial, especially the thread after this node.

I must admit i hadn't thought of a solution to that problem successfully before, although i've had this problem whenever i really wanted to subclass a module i didn't write. It wasn't possible, because whatever new attribute i introduced, i could not be sure it wouldn't break something with the next version of the Superclass...

Abigail-II's solution does not only seem to solve that, it does solve the problem. As pointed out in posts in the above mentioned discussion, this technique makes it neccessary to use accessor/mutator methods, which is what we want when we use OO.

So I've come to the conclusion that these "Inside Out" objects (as Abigail calls them) are the better approach when you want subclassable OO. There's no need to rewrite classes that use the popular way, most classes aren't subclassed anyway, but if you wanted to subclass GD::Image you'd have a problem: how to call your member variables?

The next step

There are some weaknesses that might seriously stop you from thinking the solution is useful. But wait: They're there because it's not the popular way.

My contribution is an RFC: A superclass for all classes using that new methodology to solve at least four of these problems: the DESTROY method, the lexically limited data-variables, the serialization issue and finally the methodmaker. Probably the stringification problem can be solved generally in that class.

I have already posted an idea in the disucussion, but this already has developed and here comes the result:

package OO; use strict; use warnings; use Carp qw/croak/;

This is the first question: the name, how should that superclass be called? 'OO' is just a thought and probably no too good idea.

#-------------------------------------------------------------------- # The registry itself: storing all the data of your objects #-------------------------------------------------------------------- my %Object = (); # Object => Class => Attribute

This is not a question, but the idea: Store all the information right here in that lexical. A question is the structure: Class,Field,Object, which would more directly inherit Abigail's idea or Object,Class,Field, which has the advantage that the destruction can be done easily without tricks or search.

#-------------------------------------------------------------------- # inheritable constructor with generalized behaviour #-------------------------------------------------------------------- sub new { my $class = shift; my $self = bless [caller], $class; # is that neccessary if you can override new()? if( $self->can( 'initialize' ) ){ $self->initialize( @_ ); } return $self; }

Should there be something like this constructor at all? You see, it is quite unneccessary, but what else should a constructor do and why should every class write something similar?

#-------------------------------------------------------------------- # Use these two methods to get and set members of your # object and they will do encapsulation for you # # BE CONSISTENT our your OO will BREAK! # #-------------------------------------------------------------------- sub oo_get { my $obj = shift; my $field = shift; # member hash is based on caller class # and may be overwritten by third argument to get() my $class = @_ ? shift : caller; $Object{ $obj }{ $class }{ $field } } sub oo_set { my $obj = shift; my $field = shift; my $value = shift; my $class = @_ ? shift : caller; $Object{ $obj }{ $class }{ $field } = $value; }

These two are the crucial point of the whole thing: They get/store attribute data depending on the caller. What? Yes. These two are not to be used anywhere except in the package that defines a class. This way each class has it's own data slot as the idea is.

The names of these two must be carefully chosen, but probably 'oo_' can be avoided by classes easily.

#-------------------------------------------------------------------- # most important: DESTROY #-------------------------------------------------------------------- sub DESTROY { my $obj = shift; delete $Object{ $obj } #that's why the structure of the regist +ry was chosen like that }

Jep, that's all about the possible memory leaks. Object deletion causes object data deletion. by having this in the superclass, you needn't rewrite it in every class.

#-------------------------------------------------------------------- # create_accessor class method to create simple accessor/mutator + methods #-------------------------------------------------------------------- sub oo_create_accessor { my $pkg = shift; no strict 'refs'; # we're messing around with the symbol table foreach my $mem ( @_ ){ my $symbol = $pkg . '::' . $mem; if( defined *{ $symbol } ){ croak "Attempt to redefine $symbol via create_ +accessor"; } else { *{ $symbol } = sub { my $self = shift; if( @_ ){ $self->oo_set( $mem , $_[0] , +$pkg ); } else { $self->oo_get( $mem , $pkg ); } }; } } }

This is clear and is subject to be made more advanced but shows that it's easy to create accessor/mutator methods ala MethodMaker.

#-------------------------------------------------------------------- # debugging function/method (as you like it) #-------------------------------------------------------------------- sub oo_registry { return \%Object } 1;

further steps

Now one can write a serialization/deserialization method in that class. The stringification thing can be helped out using overload::StrVal as proposed by adrianh.

I hope this RFC is an inspiration to some as Abigail's post was inspiration to me.

--
http://fruiture.de