develooper Front page | perl.perl6.language | Postings from October 2005

subclassing associated classes elegantly

Thread Next
From:
Darren Duncan
Date:
October 19, 2005 15:11
Subject:
subclassing associated classes elegantly
Message ID:
p06230900bf7c6284b64a@[192.168.1.101]
I'm in a long-standing situation with my module development where I 
want to design a set of associated classes such that invocations of 
submethods or class methods, such as new(), of one class by another 
class continue to work as expected when any or all of those classes 
is subclassed.  I have a solution already that works, but I'm looking 
for the most optimal solution.

An example of when this situation can arise is if person X implements 
a simplified XML DOM implementation using 2 classes, Document and 
Node, that work together, where one of those classes (Document) can 
create objects of the other (Node), and person Y wants to subclass 
the XML DOM implementation, meaning that those same Node objects made 
by one of person Y's Document subclass should be objects of person 
Y's Node subclass.

To illustrate, say we had these 4 classes (the syntax may be wrong):

   # This is one associated class set:

   class A {
     submethod one () {
       return 'hello';
     }

     submethod two () {
       B.four();
     }
   }

   class B {
     submethod three () {
       A.one();
     }

     submethod four () {
       return 'here';
     }
   }

   # This is separate and optional associated class set:

   class C is A {
     submethod one () {
       return 'world';
     }
   }

   class D is B {
     submethod four () {
       return 'there';
     }
   }

What I want to be able to do is set things up so that user code can 
do something that is effectively like this:

   my $first = A.two(); # returns 'here'
   my $second = B.three(); # returns 'hello'
   my $first = C.two(); # returns 'there'
   my $second = D.three(); # returns 'world'

The situation is that classes C and D represent any arbitrary named 2 
classes that are subclassed from A and B, and so the latter can't 
know the names of the former, and the latter have to work 
independently of C and D also.

This is one variant of a solution I have come up with:

   # This is one associated class set:

   role AB {
     submethod name_of_class_A () {
       return 'A';
     }

     submethod name_of_class_B () {
       return 'B';
     }
   }

   class A does AB {
     submethod one () {
       return 'hello';
     }

     submethod two () {
       .name_of_class_B().four();
     }
   }

   class B does AB {
     submethod three () {
       .name_of_class_A().one();
     }

     submethod four () {
       return 'here';
     }
   }

   # This is separate and optional associated class set:

   role CD {
     submethod name_of_class_A () {
       return 'C';
     }

     submethod name_of_class_B () {
       return 'D';
     }
   }

   class C is A does CD {
     submethod one () {
       return 'world';
     }
   }

   class D is B does CD {
     submethod four () {
       return 'there';
     }
   }

This is another variant of a solution I have come up with:

   # This is one associated class set:

   role AB {
     submethod invoke_one () {
       return A.one();
     }

     submethod invoke_four () {
       return B.four();
     }
   }

   class A does AB {
     submethod one () {
       return 'hello';
     }

     submethod two () {
       .invoke_four();
     }
   }

   class B does AB {
     submethod three () {
       .invoke_one();
     }

     submethod four () {
       return 'here';
     }
   }

   # This is separate and optional associated class set:

   role CD {
     submethod invoke_one () {
       return C.one();
     }

     submethod invoke_four () {
       return D.four();
     }
   }

   class C is A does CD {
     submethod one () {
       return 'world';
     }
   }

   class D is B does CD {
     submethod four () {
       return 'there';
     }
   }

In either case, the expectation here is that the submethods of role 
CD will override those of role BC regardless of which class' other 
methods invoke those, when the invocant class is C or D.

So I'm wondering what is the best way to develop my associated class 
sets such that it is easiest for third parties to be able to 
subclass-extend them.  Should I use one of the two solutions above 
(both of which have been tried in real life, in Perl 5, the second 
more recently)?  Or is there another solution that is better than 
both?

Also, in such a situation as the above, is it reasonable to support 
easy subclassing, or would it be better to avoid that complexity and 
instead expect users to create objects that wrap the others instead 
of subclassing them?

Assume also that it may be counter-productive for one class to expect 
user code to invoke the second class on its behalf, such as if when 
pair of classes is hidden behind a second pair of classes that 
mediate access to them.

What are some best practices here that can be used by anyone faced by 
a similar problem?

-- Darren Duncan

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About