Jump to content

OOP Databases Singletons and Registries


Recommended Posts

I've been doing PHP for over 4 years now - but only lately (last 12 months) been starting to write it as OOP.  I came to the conclusion this week that my code cannot be performing efficiently at all and I've tried to research how to do it better.

 

Various people mention registries and singletons - but i've no idea what these are - even after reading about them!  I don't quite understand how they work - but I think I understand their purpose - and I think their purpose is exactly what I want.

 

I _think_ that a database class as a singleton gives an application a single point of call to a database, and drastically reduces the amount of connections.

 

currently my db access in oop goes a little like this:

 

class db{} //this has the mysqli->connect statement and thats all

 

class user($db){}

//construct sets $this->db = $db;

//this has lots of queries that take place in this class e.g.

$db_query = $this->db->mysqli->query("select * from "); //only a dummy query!!

 

I have several class like user each one passing in the $db, and putting queries onto it.  In my head that seems like a lot of memory is being used - especially as there are usually 3 or 4 classes - as well as the DB class being used on each page - I think that might result in 4 statements of $this->db = $db;

 

this surely can't be right and singletons / registries sound like what i need.

 

can anybody explain to me a) what a singleton is b) what a registry is and c) how to effectively implement them via short code sample so I can an idea.  I've seen people writing "getInstance()" functions - again I've no idea what they are so if that can be summarised too I'd be an extremely happy person!

 

Link to comment
Share on other sites

A singleton is an object with two primary characteristics:

 

1. It's a global

2. Only one instance of that particular singleton can exist at any one time

 

If you've been reading these forums, you've probably read the mantra "globals are bad."  Why are they bad?  They're bad because they tend to break encapsulation, and they promote tight coupling of an object to the greater system it resides in.  In other words, relying on globals can make code harder to debug, less modular, and just a pain to deal with in general.  Raw globals can be rewritten at any time, and there's no way to ensure that the global you want is the one you're currently using.

 

Singletons mitigate this somewhat.  Their structure (which you'll see below) only allows one to exist.  Also, since they're objects, you can enforce a public interface so that their internal values aren't overwritten by accident.  In the most general sense, a singleton looks like:

 

class Singleton
{
   private static $instance;

   private function __construct() {}

   public static function getInstance()
   {
      if(!self::$instance)
      {
         self::$instance = new self();
      }

      return self::$instance;
   }
}

 

Obviously, other methods can be added to this, and both getInstance and the constructor can take arguments if you need them to.

 

Registries are somewhat different as they serve a different purpose.  The whole point of a registry is for you (or the system itself) to record and store values for future use.  Think of a site that allows 3rd party plugins - they need to be registered with the system in order for the system to realize they're available for use.

 

Where does a singleton enter the picture?  Registries are often represented as singletons.  The reason being that you can have different types of registries (one that stores request data, one that stores references to 3rd party plugins, etc), but you most likely only want one of each of those, as any more would be redundant.  And, you probably want to be able to access those registries at any time as there's no telling exactly when you'll be reading/writing to a registry, so having them globally available is desirable.

 

I hope this helps.

Link to comment
Share on other sites

Nightslyr, thanks for the clear cut explaination. Fortunately i'm in final year of a software engineering course doing about code management and know about coupling/cohesion.

 

would using the singleton approach be appropriate, or the must appropriate approach for a database object then? Or would this still create many connections on a single page.

 

For obvious reasons I want to keep the connections as low as possible, whilst still accessing all the data I need.

Link to comment
Share on other sites

Nightslyr, thanks for the clear cut explaination. Fortunately i'm in final year of a software engineering course doing about code management and know about coupling/cohesion.

 

would using the singleton approach be appropriate, or the must appropriate approach for a database object then? Or would this still create many connections on a single page.

 

For obvious reasons I want to keep the connections as low as possible, whilst still accessing all the data I need.

 

Using a singleton for a db object is pretty common practice.  Whenever you want to use it, you simply store a reference to it in a variable by calling getInstance:

 

$myDBO = Database::getInstance();
$myDBO->query(/* some query */);

 

Since there can be only one instance of any given singleton class in the system, you'll have only one connection to the database.

 

Something to consider - db initialization.  When you first instantiate your database singleton, you'll probably want to pass in the relevant database connection info.  There are a few ways to do it, but I think the simplest would be to store them in an associative array, and pass that into getInstance.  We can make it (and the constructor) flexible by using default argument values:

 

private function __construct($settings = null)
{
   if($settings)
   {
      /* store each setting in the data member variables */
   }
}

public static function getInstance($settings = null)
{
   if(!self::$instance)
   {
      self::$instance = new self($settings);
   }

   return self::$instance;
}

 

With this, you're not forced into passing db setting info to getInstance every time you want to manipulate the db.  Instead, you'll only need to do it in the first invocation of getInstance, when the object is actually constructed.

Link to comment
Share on other sites

ok i think i get it. just to confirm though...the constructor in the database class where the:

 

if($settings){} statement lies...

 

thats where i make the initial database connection so that the class would look a little like this:

 

class Database
{
   private $db_connect;

   private function __construct($settings = null)
   {
      if($settings)
      {
         //unserialize $settings array etc first...then...
         $this->db_connect = new mysqli($domain, $username, $password, $db_name);
      }
   }

   public static function getInstance($settings = null)
   {
      if(!self::$instance)
      {
         self::$instance = new self($settings);
      }

      return self::$instance;
   }
}

 

 

just a quick aside, I tend to hold my settings hardcoded in the database class, so that i don't have to pass in "settings", is this advisable? obviously in the constructor i could test for a boolean variable so if settings==true then /* store each setting in the data member variables */

Link to comment
Share on other sites

Well, one of the points of OOP is to make code modular.  So, you should do what you can to make it so you can plug your db class into a variety of projects.  That's why I suggest passing in some form of settings data as an argument during instantiation.  In most real world scenarios, info like db connection settings are read in from a separate file.  That way, instead of having to muck with actual system code in order to get this class to work on separate projects, all you'd need to modify is a non-system settings file.

Link to comment
Share on other sites

A database wrapper, though often used as an example, is in my opinion not a good candidate for using the singleton pattern. What if you later find that you need to open another connection? If that's the case then you're out of luck as you've designed your system to only support one connection. You've coded to an interface for the singleton and you would have to go through all your code to migrate to another interface. That could turn out to be very time consuming. You can't say now that you probably won't need it unless you have the ability to look into the future.

 

One of the strengths of OOP is that you are making your code modular and reusable, but by using the singleton pattern you are enforcing a particular type of usage, which locks you in, and which couples you very tightly.

 

Consider the differences between these methods:

public function something()
{
$db = Database::getInstance(); // using singleton
$db->doStuff();
}

public function something(Database $db) // passed by argument and using type hinting
{
$db->doStuff();
}

 

There is one fundamental difference between these two methods even though they do the same. The first one is limited to always using a Database. The second one can use descendants of Database. If you have another class CustomDatabase that extends Database then you could use that one in variant two, but not easily in the first variant.

 

One of the most important design principles is that you should code to an interface, not to an implementation. The first variant of the method fails to obey this principle because it automatically assumes that it will be specifically a Database and not potentially any descendant of that. The first one can only be changed by going through the code and changing it, i.e. it's determined at compile time. The second one can be changed dynamically at run time, which design wise is much better because it means it will have a looser coupling.

 

The singleton pattern is in my opinion way too overused. I suppose it's because it's conceptually pretty easy to comprehend and it's also easy to implement and it gives you easy access. This easy access has several quite serious drawbacks though. Just because it's a pattern doesn't mean it's a good pattern nor that you should use it. I can't think of even one good instance where a singleton would be a better option than passing an object by argument.

 

I'm not too much of a fan of the registry pattern either. It's mainly because it's stores elements globally. It's difficult to ensure the integrity of global objects. From within a class you can't know who have touched it, and you can't know if it contains the correct value anymore. In most registry implementations people don't check values that are put in the registry. Most people just create a registry that takes all possible data. I don't like the idea of having one big hole where you dump everything into. Again, I think it's a much better idea to pass by argument.

 

There will always be a way of writing OO code without using neither a singleton nor registry, and I think it would be better to do that.

Link to comment
Share on other sites

Copy data from one server to another. Read data from an external server without disconnecting from local server. As I said, what you need right now doesn't matter in this instance because you can't know what you need in the future. For that reason you need to design a system that doesn't lock you in. Even then there is still the coupling issue that needs to be taken into account.

Link to comment
Share on other sites

good topic ...while we are on it what is the usage of registries?  You can't pass the values from script to script without a database or a flat file or session etc..so why use it?  This is probably a stupid question, but I never used a registry before.

Link to comment
Share on other sites

The idea of a registry is to have a gigantic hole to dump data in and take data out of in runtime. Things like configuration information or other things that are often used throughout the entire application. It's a bad idea though.

Link to comment
Share on other sites

ok just to continue the naivity - _how_ would a singleton be avoided in a multi server setup. and how is a registry avoided in a multi server setup?  I'm actually thinking of an application I'm starting to draw requirements for that would require a multi server setup (multi database servers, multi file servers)

Link to comment
Share on other sites

i took from your saying of not using singletons for databases as you may need to use more than one connection to mean that in a multi server setup it provides a lockin that you don't need - i.e. using a singleton class limits you to one connection - which is a bad thing in a multi server setup...

Link to comment
Share on other sites

Locking you to one connection per process is a bad idea in general because you cannot know if you at a later point will need to open another connection. If that turns out to be the case then that will be impossible without extensive modification of the existing code. If you've locked yourself in then your code is not portable.

Link to comment
Share on other sites

You would avoid it by not even thinking about it!( :-P ) Creating something to be a Singleton or a Singleton Registry is somethign very specific that you would "go out of your way" to create, it just doesn't happen.

 

I think you would benefit greatly by google-ing some "PHP Design Patterns".

 

Maybe a factory pattern to generate multiple connections, with an array as parameter containing connection details...

 

If your application calls for it, maybe a MVC (Model View Controller) pattern?

 

Here's a link to a few more Design patterns...

http://www.fluffycat.com/PHP-Design-Patterns/

 

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.