At the risk of starting an OO war... In general, I have found that subclassing is not a very good idea when using other authors modules (such as CPAN modules), because one has to be very careful in your sub-class to not overwrite a key, that that has a different meaning in the super-class... and this could change as new versions of the sub-class are installed.
I have found that containment (a HAS-A relationship) combined with an AUTOLOAD function which delegates undefined functions to the contained object instance (in this case a dbh), is far safer, easier, works well, gives you your own class heirarchy, and avoids all these problems. I think that inheritance is far OVER used in many OO systems. Just a thought. Lincoln -----Original Message----- From: Tim Bunce [mailto:[EMAIL PROTECTED]] Sent: Friday, January 04, 2002 12:08 PM To: [EMAIL PROTECTED] Cc: Tim Bunce; Matt Sergeant Subject: Subclassing - draft docs (Re: Important: Subclassing and Merging DBIx::AnyDBD into the DBI) Here are some drafy docs for subclassing (partly based on a patch sent by Stephen Clouse). Comments welcome. I'll add some words about what you could do, and should not do, by subclassing. And about multiple levels of subclassing. Tim. =head2 Subclassing the DBI DBI can be subclassed and extended just like any other object oriented module. Before we talk about how to do that, it's important to be clear about how the DBI classes work together. By default C<$dbh = DBI->E<gt>C<connect(...)> returns a $dbh blessed into the C<DBI::db> class. And the C<$dbh->E<gt>C<prepare> method returns an $sth blessed into the C<DBI::st> class (actually it simply changes the last for characters of the calling handle class to be C<::st>). The leading 'C<DBI>' is known as the 'root class' and the extra 'C<::db>' or 'C<::st>' are the 'handle type suffixes'. If you want to subclass the DBI you'll need to put your overriding methods into the appropriate classes. For example, if you want to use a root class of C<MySubDBI> and override the do(), prepare() and execute() methods, then your do() and prepare() methods should be in the C<MySubDBI::db> class and the execute() method should be in the C<MySubDBI::st> class. To setup the inheritance hierarchy the @ISA variable in C<MySubDBI::db> should contain C<DBI::db> and the @ISA variable in C<MySubDBI::st> should contain C<DBI::st>. The C<MySubDBI> root class itself isn't currently used and, apart from setting @ISA to include C<DBI>, it should be left empty. So, having put your overriding methods into the right classes, and setup the inheritance hierarchy, how do you get the DBI to use them? You have two choices, either a static method call using the name of your subclass: $dbh = MySubDBI->connect(...); or specifying a C<RootClass> attribute: $dbh = DBI->connect(..., { RootClass => 'MySubDBI' }); The only difference between the two is that using an explicit RootClass attribute will make the DBI automatically attempt to load a module by that name (and not complain if such a module can't be found). If both forms are used then the attribute takes precedence. Here's a brief example of a DBI subclass. A more thorough example can be found in t/subclass.t in the DBI distribution. package MySubDBI; use strict; use DBI; use vars qw(@ISA); @ISA = qw(DBI); package MySubDBI::db; use vars qw(@ISA); @ISA = qw(DBI::db); sub prepare { my ($dbh, @args) = @_; my $sth = $dbh->SUPER::prepare(@args) or return; $sth->{private_mysubdbi_foo} = 'bar'; return $sth; } package MySubDBI::st; use vars qw(@ISA); @ISA = qw(DBI::st); sub fetch { my ($sth, @args) = @_; my $row = $sth->SUPER::fetch(@args) or return; do_something_magical_with_row_data($row) or return $sth->set_error(1234, "The magic failed"); return $row; } You can stash private class data in a handle and carry it around via C<$h->E<gt>C<{private_..._*}>. See the entry under L</ATTRIBUTES COMMON TO ALL HANDLES> for info and important caveats. When calling a supermethod that returns a handle, be careful to check the return value before trying to do other things with it in your overridden method. This is especially important if you want to set a hash attribute on the handle, as Perl's autovivification will bite you by (in)conveniently creating an unblessed hashref, which your method will then return with occasionally baffling results later on. It's best to check right after the supermethod call and return undef immediately on error, just like DBI would and just like the example above. If your method needs to record an error it should call the set_error() method with the error code and error string, as shown in the example above. The error code and error string will be recorded in the handle and available via C<$h->E<gt>C<err> and C<$DBI::errstr> etc. The set_error() method always returns an undef or empty list as approriate. Since your method should nearly always return an undef or empty list as soon as an error is detected it's handy to simply return what set_error() returns, as shown in the example above. If the handle has C<RaiseError>, C<PrintError>, or C<HandleError> etc. set then the set_error() method will honour them. This means that if C<RaiseError> is set then set_error() won't return in the normal way but will 'throw an exception' that can be caught with an C<eval> block. =cut ----- End forwarded message -----
