No, I wasn't listening, this is my first time really looking at PerlMonks. I was in the middle of mucking about with Tie::Cache::LRU and found myself writing a virtual base class and all these enforcement methods and such. So rather than make a one-off (heavens, no!) I slapped together Class::Virtual.

So first off, Class::Virtual *doesn't* require each subclass to call a check function directly (although re-reading the documentation, I can see why one would think that. Will correct.) The check methods are there for code auditing purposes, reporting, internal use and subclasses of Class::Virtual (as we'll see in a moment). I'll change the docs to de-emphisize them and emphisize the real purpose of the module.

The major difference I can see between Class::Virtual and AbstractClass is C::V does its work at run-time whereas AC works at compile-time. C::V waits until you actually call an unimplemented virtual method before it yells. This might seem silly (since it would have blown up anyway) but it does provide a more informative error. Also, doing the checks at compile-time would cause half my modules to blow up (being the poster boy for method auto-generation that I am). However, not to be outdone...

package Class::Virtually::Abstract; use base qw(Class::Virtual); use Carp::Assert; assert( prototype('CORE::bless') eq '$;$' ); sub bless ($;$) { my $class = $_[1] || caller; if( grep { $_ eq 'Class::Virtually::Abstract' } @{$class.'::ISA'} +) { confess("Connot bless objects into abstract class $class"); } else { CORE::bless(shift, $class); } } sub import { shift; my $base = caller(); *{$base.'::bless'} = \&bless; *{$base.'::import'} = sub { my $class = shift; return if $class eq $base; foreach my $missing_meth ($class->missing_methods) { require Carp; Carp::croak("Class $class must define $missing_meth for cl +ass ". "$base"); } $class->SUPER::import(@_); }; 1; }
That should emulate the compile-time behavior of AbstractClass and its mucking with bless(). (BTW Your prototype is wrong). And feel free to steal the closure trick to generate the import() instead of the eval(). I can flesh this out an ship it with Class::Virtual if you'd like.

So, digging into the internals... a few things bother me about AbstractClass. First, it hijacks import(). Why should an OO module need an import() routine? Well, AbstractClass needs one. Lots of hybrid OO/functional modules need it (Class::Fields for one). Its too important to take away.

Another problem with the import() route is its brittleness. Consider the following...

package Foo; require Some::Abstract::Class; @ISA = qw(Some::Abstract::Class);
Ooops. And this is a perfect valid and common, way of subclassing which defeats AbstractClass. Consider this, too...
package Foo; use base qw(Some::Abstract::Class);
base.pm does not call import() (and rightly so). AbstractClass defeated.

Another, @ABSTRACT_METHODS bothers me. Magical global variables bother me, doubly so in OO contexts. Triply so when you start putting this magical global into other packages. Maybe if you put it into @{$base.'::__ABSTRACT_METHODS'} or something. I take the class data route instead.

Overriding bless... I haven't decided if that's Evil or not. I'd rather define a default new() method that does the same thing. Besides, even if you do manage to generate an object from a virtual class, its going to explode the first time you use it.

Another, with AbstractClass, SomeAbstractClass->can("some_virtual_method") returns false. With Class::Virtual it returns true (but if you try to use the method returned it will explode). I guess this is a design decision.

I don't totally understand why AbstractClass doesn't work with MI, but Class::Virtual doesn't have any problem (god forbid, I can't live without MI).

Finally, in the Class::Virtual scheme, virtual base classes are subclasses of Class::Virtual. I like this because of its synchronicity and because it allows the behavior of Class::Virtual to be altered (as for Class::Virtually::Abstract).


In reply to Re: AbstractClass by Anonymous Monk
in thread AbstractClass by tilly

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.