http://www.perlmonks.org?node_id=80095


in reply to Re (tilly) 3: Object Oriented Pattern help
in thread Object Oriented Pattern help

I was talking about correctly scoping an object, this would involve the object being constructed whilst in scope and destroyed while out of scope. Now my point was that in this case as the scope is well defined so should be used. Now as database connections represent a scarce resource, keeping a connection open too long is not IMHO desirable.

If I were to solve a problem such as this I suppose I would take the middle ground. The point you make about complex initialisation is very valid, however I would disagree that this is something that the singleton pattern is best suited to resolve. I would suggest the factory pattern would be more appropriate here. I would wrap the database connection up in object that is scoped over the life of the query, and have this object constructed by the factory. I would probably make the query an object also, complete the query close the connection. If performance was really an issue and opening and closing connections was a concern I would NOT extend the scope of a connection DIRECTLY. I would implement connection pooling withing the database connection object via a faceted object (pattern not seen within DP but described in C++ for proffessional programmers, kinda like a cross between a factory and a singleton, an object that can doll out several of a suite of objects within it's control). In short my point was that the use of a singleton in this case covers up weaknesses in the design that could be resolved more elegently.

UPDATE I just want to say that I am not actually disagreing with tilly in this post. His comments are all valid (except the one where he disagrees with me *grins*) I think my views actually fit alongside his. This post is to expand upon my thinking not to create an argument.
--

Zigster

  • Comment on Re: Re (tilly) 3: Object Oriented Pattern help

Replies are listed 'Best First'.
Re (tilly) 5: Object Oriented Pattern help
by tilly (Archbishop) on May 14, 2001 at 01:27 UTC
    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...

      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

        Here are my basic premises.

        1. If you have to write more code, and it takes longer to describe, then it is more complex. No ands, ifs, or buts.
        2. 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.
        3. 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...