I fail to understand what you think a more complex
approach buys.
I know the various things you are discussing, of
course. But I fail to see what problem they are
intended to solve above the simple approach.
For instance consider your factory object
suggestion. Well a database handle is already an
object wrapped around a connection. What do I
need another for? Since I already have the
intialization logic nicely wrapped, it would be
trivial for me to implement that whenever I wanted
if, for instance, I needed to add some logging
facility. Or if I needed to prevent people from
trying to inappropriately close the connection.
(Which is something I would prefer to do by just
never calling close.) So I don't have to do
anything now to get that upon need. So what need
do I have to do it now?
As you say, database handles are a limited
resource. But suppose I am running my web-server.
Well I can control how many database handles are
available, and I can control how many Apache
instances are active. As a first pass you solve
that problem by having more possible database
handles than Apache instances. In a non-forking
environment it would take a lot of work to get
any win from your idea of allocating connections
out of a fixed pool of available connections. Oh
it is possible. You could play with
IPC::Shareable for dynamic allocation of
connections to processes. But you can get from
the simple idea to that whenever you need to.
Again, why do it now? What is the win?
In case it isn't obvious, my desire here is for
the simplest API that solves the simplest problem
that needs to be solved now, but doesn't limit my
options later. A global variable may solve the
problem, but limits the ways in which I can choose
to extend it. Creating a function from which you
can get something that works as an initialized
database handle is something that solves the
problem but doesn't limit what I can do later.
Now the first pass is to open a connection every
time. I can tell you from both theory and
experience, both for myself and others, the simple
approach hits a major performance issue because
opening database connections to a relational
database is expensive. The first possible
improvement - which can be done without changing
the external API - is to memoize the connection.
The second reasonable improvement is to share
database connections across tasks. This is one
of the big reasons that people build application
servers, move to mod_perl, etc.
Beyond that there are a whole suite of things
that you can do. They can be done without
breaking the API. But while I can dream up cases
in which I might want to do them, I have never
felt the need to do it in real life.
So my question is and remains, what concrete win
are you looking to get from moving to a more
complex design up front? If there is something
that I am missing then I want to know about it
because I might need to do it. If the win is
just the ability to use more complex buzzwords to
describe what I am doing, then count me out...
| [reply] |
I fail to understand what you think a more complex approach buys.
I am not sure it is more complex, it is different certainly. IMHO the difference between the factory and singleton pattern is simply that the factory object constructs an object that it no longer has a handle too while the singleton preserves a handle to enable global access. You are correct that there is no good reason to wrap a dbi connection in a seperate object, I was getting over-excited in an OO sense (scary picture right there). However I do stand by my criticm regarding using a singleton in this context. There is no reason to give global access so you should not.
The ONLY advantage of my more complex design is in design purity. IMHO it is well worth coding effort to show intent, and even to enforce intent. OO programming advocates this at many levels. The patterns in OO programming are useful only if they are used in the way intended, my original node described the intent for a singleton. It is my belief that in this case a singleton resolves many of the technical issues but does not clearly show intent.
Singleton Intent Ensure a class only has one instance and provide a global point of access to it.
There will not be one instance of the connection class and a global point of access is only required to work around a scoping problem. The factory and faceted combination would seem to be closer to the requirement.
In my mind an implementation of a factory and facteted combination would require VERY little additional coding effort.
Update
Just to address some of the points I missed first time around ;-) Pressed submit instead of preview.
I must first confess to ignorance, you talk about causing the webserver to control the number of database handles that are availible. I have no experience of doing this. I have always controlled connection pooling at an application level, but then most of my dev is in large scale app land not web stuff. I will therefore bow to your superior knowledge here. My instincts tell me that I would still like to control them directly but I cannot justify it technically as I know little of the pros and cons.
--
Zigster
| [reply] |
Here are my basic premises.
- If you have to write more code, and it takes longer to
describe, then it is more complex. No ands, ifs, or buts.
- I value simplicity more than abstract theories about
correct ways to do things. My theory is that
simple is easier to write, easier to learn, and easier
to figure out how to change if you need to. I will need
something concrete to get me to throw away advantages
like those.
- I am not a big label person. Oh, I like knowing what
the labels are, but I generally avoid throwing them
around without good reason.
So by these criteria I darned well know what I will do.
An incidental point. Your label is wrong. As I said I am
not a big label person, so I don't really care. But for
this to be a singleton design pattern, you need to have a
class involved. Without a class this is plain old
memoization. And plain old memoization has as its purpose
allowing you to do work only once and then return the
answer from cache. Which documents exactly our intent.
Don't believe me? Well look up Memoize. With that
you could write the DBI method like this:
use Memoize;
memoize('dbi');
sub dbi {
DBI->connect(yadda, yadda);
}
instead of like this:
{
my $dbh;
sub dbi {
$dbh ||= DBI->connect(yadda, yadda);
}
}
except that in one case we have let Dominus write a
general caching mechanism, and in the other we rolled our
own.
In fact with this insight, the dbi function might well
take an optional argument specifying which of several
databases to connect to. Conceptually this is still
simple memoization. The case where we always returned the
same thing is just the simplest case. With a little work
we could return a connection to the database of your
choice instead, defaulting reasonably. Now do you see why
this is different from the Singleton design pattern? I
have not imposed any structure on how the caching works,
so I can move to a more complex structure without adding
related structures.
And here is why I don't much like labels. Labels
blind you. Labels turn into categorizations that are
applied with little thought and foresight. This reduces
the myriad of ways to understand problems into memorized
channels that shall never be deviated from. I try not to
limit myself like that.
This tendancy is most of what I dislike about OO
programming and OO design. I only rarely read
/., but I did last week when
people were talking about an article of chromatic's.
And so I ran into
this
little gem. Even though it is from an anonymous coward,
I believe it is probably genuine. Not only is it written
too well to be a typical troll, it also fits too
well with things that I have seen... | [reply] [d/l] [select] |