OO Architecture
[email protected] (Tim Bunce) Fri, 23 Sep 2005 13:16:47 +0100
| Newsgroups | perl.dbi2.dev |
|---|---|
| Message-ID | <[email protected]> |
[Sorry for the delay in replying. I'm extra busy at the moment, not
least because my wife is just a few weeks away from giving birth to our
second child.]
On Sat, Sep 10, 2005 at 12:58:33PM +0200, Jochen Wiedmann wrote:
> Hi, Tim,
>
> I believe that the time for asking this is appropriate, because you
> seem to be in a phase of basic considerations: Are you planning that
> DBI 2 will still have what I call its own OO implementation?
It's basically a distinction between:
"An application DBI handle HAS-A driver handle"
and
"An application DBI handle IS-A driver handle"
It's a bit odd in DBI v1 because the application and driver handles are
not clearly distinguished (they're the "inner" and "outer" handles I
talk about in my tutorials) and are both blessed into the same class.
The method redispatching is controlled via a $h->{ImplementorClass}
attribute. Umm, yes, it is a bit odd.
> In case that my term doesn't clearly tell what I mean: I am referring
> to the way, how DBI sits both on top and below the driver, database,
> and statement handles. Or, in other words, I am talking about the fact
> that what the driver returns as a database handle (for example, an
> instance of DBD::mysql) isn't, what the driver user actually sees (but
> an instance of ... heck, what is it, I do not exactly remember, ...
> DBI::db? DBI::_::db?, whatever.
I think that's not central to the issue so I won't expand on it.
(Let me know if you'd like me to.) Read on to see what I mean.
> I remember, when we had a discussion on the subject that speed was the
> reason for designing things like this. I never did any measurements
> and believe that you did, so possibly the desicions was right. But
> that decision was taken in or around 1996 and for Perl 5. Besides, I
> can hardly imagine that the difference was more than some 5 or 6
> percent: Usually the communication with the database is the limiting
> factor, because I can hardly imagine that the DBI architecture of
> stacked handles needs less memory than a basic system of subclasses
> (which internally is in place anyways).
I don't think speed was the primary issue here - assuming we're talking
about the same thing. Some implementation decisions were taken in the
name of speed but only after the bigger architectural issues were set.
(The redispatching of method calls actually adds significant overhead.)
The key question here was, and is, "how do you subclass the DBI?"
With the HAS-A approach of DBI v1 you can relatively easily subclass
the DBI and it'll work regardless of the driver being used. That's
because the handles are blessed into the same class regardless of the
driver being used.
Contrast that with JDBC where the handles are objects in the drivers
class. So a database handle might be org.apache.derby.impl.jdbc.EmbedConnection
for example. Subclassing that in a generic way is much harder.
The way many people would deal with trying to subclass random classes
that share a common interface would be to wrap the object inside another
object with a fixed class. Method calls on the wrapper object would
would then be redispatched to the inner handle. <Insert appropriate
Design Patterns terminology here.>
Well, that's just what DBI v1 has already done for you.
But here's another, deeper, question that's more relevant at this point:
"is subclassing the DBI actually important?"
I'd value the opinions of everyone on that question.
I suspect the issue will hinge on if/how Perl6 lets you dynamically
subclass an existing class by mixing in a Role and if that Role can
intercept/override calls to existing methods. But I'm hand-waving here,
as you can probably tell, as my perl6 foo isn't strong. Help wanted.
Basically if an application can say "I'd like to use this driver but
mix into it this list of Roles" then what used to be achieved via
subclassing the DBI could be done via messing with handle classes.
> I am asking, because I do see a dark side too: I always found the
> system less than obvious , diff?cult to handle, and a source of *very*
> subtle reasons, why things wheren't working as expected. I never
> worked on the DBI itself, so I cannot tell for sure, but if the work
> was more difficult for the driver author, then it is reasonable to
> guess that the same holds true for the DBI author.
>
> Thus, if possible: Use simple Perl inheritance from now, use the basic
> OO model, and nothing else.
Certainly if DBI v2 ends up using HAS-A and method redispatching then
the 'inner handle' should be a separate simple object and there'd be no
ImplementorClass attribute.
But the HAS-A vs IS-A question is still an open one. Partly because of
the "is subclassing the DBI important?" question and partly because
we'd need an alternative way to implement all the cool features that
the DBI implements as part of method redispatching.
Here's what DBI v1's method redispatching gives driver authors for free:
1 Easy subclassing
2 RaiseError, PrintError, HandleError, and PrintWarn
3 Consistent logging (especially levels 1 and 2 with method args and results)
4 Basic checking of method arguments
5 Caching of attributes
6 Error state management
7 Callbacks hooks on all methods (via recent $h->{Callbacks} attribute)
8 Profiling (DBI::Profile)
9 Automatic tainting and taint checking.
10 Thread safety
I think the best alternative to method redispatching for logging, profiling,
callbacks, and possibly some others, is wrapping/nesting of drivers.
By which I mean having a driver class that HAS-A another driver handle
and just forwards all method calls to it. Such a driver would be 'transparent'.
Then logging, for example, could be implemented by subclassing the
'transparent driver' and adding logging functionality.
Similarly for Profiling.
The application would be able to ask for a mysql driver, for example,
wrapped in a profiling driver, which is then wrapped in a logging driver.
That's enough talk from me for now. Opinions wanted...
Tim.