OOP vs. Procedural *Singleton* (edit)

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

OOP vs. Procedural *Singleton* (edit)

Post by Mordred »

(Inspired by the neighbour topic.)

Okay, am I the only one not jumping for the OOP high and staying "procedural" where common sense just demands it?

Let's take database access. In theory, a database object is a singleton. In OOP you would do the getInstance() caboodle with a code possibly like this:

Code: Select all

function BusynessLogic() {
  $pDatabase =& CDatabase::GetInstance();
  $pDatabase->Query("SELECT * FROM `table`");
  ...
}
Or, if you want to have several possible polymorphic database interfaces, you could try globals:

Code: Select all

class CBaseDatabase  {/*;)*/
  function Query($sQuery) {
    return false;
  }
};
class CMysqlDatabase extends CBaseDatabase {
  function Query($sQuery) {
    return mysql_query($sQuery);
  }
};
//choose a database type ... silly, I know, but let's pretend
if (...)
   $g_pDatabase =& new CMysqlDatabase();
else
  $g_pDatabase =& new CPostgreDatabase();

function BusynessLogic() {
  global $g_pDatabase;
  $g_pDatabase->Query("SELECT * FROM `table`");
  ...
}

As you can see, due to the limitations of the PHP syntax you cannot avoid an extra line in each busyness logic function (oh, cruel fate, had I a penny for each time I cursed why there were no user-definable superglobals a.k.a. just 'globals' in the usual languages :roll: )
Using a 'registry pattern' would only increase the amount of lines for a simple query, as would any more complicated solutions.

Well, hello 'procedural', a function is global everywhere and is a perfect replacement for all the hassles with singletons. Observe:

Code: Select all

function BusynessLogic() {
  DBQuery("SELECT * FROM `table`");
}

If you just cannot break yourself to use a (gasp) global function, you can mask it as OOP if you use a static class (poor man's 'namespace'):

Code: Select all

function BusynessLogic() {
  DB::Query("SELECT * FROM `table`");
}
I find it quite silly to use some syntactic fodder to make it look OOP-ish, so I go for the first variant. Here's a bit more of faux code to demonstrate that you can have as much OOP as you want behind the scenes (where you can afford ONCE to use the sloppy 'global' syntax for example)

Code: Select all

class CMysqlDatabase extends CBaseDatabase {
  function Query($sQuery) {
    return mysql_query($sQuery);
  }
};
//choose a database type ... silly, I know, but let's pretend
if (...)
   $g_pDatabase =& new CMysqlDatabase();
else
  $g_pDatabase =& new CPostgreDatabase();

function DBQuery($sQuery) {
  global $g_pDatabase;
  return $g_pDatabase->Query($sQuery);
}

My own implementation is a bit more flashy, it hides the query result resources, provides higher-level functions for dealing with select results etc., but everything is done by a global database object hidden behind generic DB*() functions. In that way I can change the object and have PostgreSQL instead of MySQL without changing the DB* functions themselves.
Last edited by Mordred on Sat Oct 21, 2006 4:19 pm, edited 1 time in total.
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Be watchful in this thread. The subject has been brought up before and has always resulted in flame wars. Before participating in a thread of this type you may want to search the previous threads on the topic of OOP vs. Procedural. Please.

This is a warning from a moderator that has seen this type of thread one too many times.
User avatar
neophyte
DevNet Resident
Posts: 1537
Joined: Tue Jan 20, 2004 4:58 pm
Location: Minnesota

Post by neophyte »

I want vanilla. I want chocolate. I want swirl. :P

Really.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

It seems like this thread is not about OOP vs Procedural at all, but about globals and how to manage connections to multiple databases. Am I confused?
(#10850)
alvinphp
Forum Contributor
Posts: 380
Joined: Wed Sep 21, 2005 11:47 am

Post by alvinphp »

Only in PHP is this ever argued, the rest of the world has moved on.
Last edited by alvinphp on Fri Oct 20, 2006 11:59 pm, edited 1 time in total.
User avatar
patrikG
DevNet Master
Posts: 4235
Joined: Thu Aug 15, 2002 5:53 am
Location: Sussex, UK

Post by patrikG »

arborint wrote:Am I confused?
You're not alone. We all are.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Post by Mordred »

I apologise, I haven't formulated my thoughts well, and this was indeed sounding like a hollow flame.

The thread in question Use collection of objects or single object collector is about using common functionality hidden behind class interfaces (database, error logging, configuration etc) almost all of which are in fact singletons. That is, the nature of these concepts is that they are (should be) unique for a given PHP invocation.

I argue that the OOP solutions, that is, the proposed Design Patterns as implemented with PHP's (limited) syntax are cumbersome to use, and that hiding the singleton behind some function calls reduces the syntactic complexity surrounding the other implementations. In the same time nothing prevents the actual implementations from benefitting from the OOP features (as illustrated by the database implementation example)

In short: It is easier to use procedural wrappers for objects which are unique for your application.

Is this making any sense now?
User avatar
patrikG
DevNet Master
Posts: 4235
Joined: Thu Aug 15, 2002 5:53 am
Location: Sussex, UK

Post by patrikG »

Paraphrasing: because of PHP's OOP implementation, globals are preferable to singletons. Would that be a correct summary of your above post, mordred?
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

The thread in question Use collection of objects or single object collector is about using common functionality hidden behind class interfaces (database, error logging, configuration etc) almost all of which are in fact singletons. That is, the nature of these concepts is that they are (should be) unique for a given PHP invocation.
None of the classes noted need be unique, the Registry may store one or many of any object. It just needs to be instantiated and stored with an identifying key. They don't actually match the Singleton Pattern since you can have > 1 instances. Why should a MySQL class be a singleton for example? A script can connect to several databases at the same time if ever a need arises... There could be several config files in a system... You can have several log formats and contexts...
I argue that the OOP solutions, that is, the proposed Design Patterns as implemented with PHP's (limited) syntax are cumbersome to use
Define cumbersome. I'm not sure I follow your logic. These objects would typically have a well defined public interface. What's the advantage of wrapping this with procedural code? That's an additional global abstraction that defeats the point of being careful in how you make objects aware of other object interfaces. I could be dead wrong in interpreting you, but function calls are global by nature and therefore carry all the disadvantages of any global variable/object...

Passing a simple Registry into objects many would argue is simple, effective, and an improvement over using Singletons or Globals or Procedural functions.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Post by Mordred »

patrikG wrote:Paraphrasing: because of PHP's OOP implementation, globals are preferable to singletons. Would that be a correct summary of your above post, mordred?
Globals functions (not globals) are a preferable wrapper to singletons. But yeah, that's the core of it, and the culprit is PHP's partial lack of syntax regarding global objects and namespaces.
Maugrim_The_Reaper wrote:Define cumbersome.
Requiring additional initalizations at least once per busyness logic function, or (depending on implementation), additional syntactic fodder to achieve a simple function call. Note that my critique is against the proposed syntaxes, not against the underlying design patterns and choices. You may think of using the function wrappers as a syntactic transformation, nothing else. Refer to my above examples using a singleton (in design patterns sense), a global variable, a global function call and a faux namespace call (implemented as a static method)
Maugrim_The_Reaper wrote: (...) function calls are global by nature and therefore carry all the disadvantages of any global variable/object
Name one. A singleton is nothing more than a glorified global object, and keeping objects in a registry is nothing different that taking the global namespace and moving its contents into another namespace (with a more inconvenient interface to use on top of that).

The advantages of using the said syntactical transformation is that you achieve the same goal in fewer steps/syntax tokens/characters. This is what makes it ... ummm ... less cumbersome ;)
Maugrim_The_Reaper wrote: That's an additional global abstraction that defeats the point of being careful in how you make objects aware of other object interfaces.
The function wrappers become the interface of the objects. The underlying implementation can still use objects to achieve the desired stratification - as the ability to change the underlying database system in my example.

Again, it is my fault of being too generic when saying "OOP vs procedural" - in fact I now realise that using global functions can still be OOP, as is the case with using them to wrap the public interface of a class.

Btw (it is really offtopic), you said
Maugrim_The_Reaper wrote:None of the classes noted need be unique.
I didn't argue that, I used them as an example of several singleton-like objects in a system, as did the OP of that thread. I agree that in some circumstances you may want to use several of these simultaneously, which no longer makes them singletons. My argument was directed at the case when the objects are logically singletons.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

Btw, why would one prefer usage of generic registry over usage of simple associative array? I mean, registries are usually come in two flavors, singleton (or static) registries and registries that passed as an argument.

Static registry:

Code: Select all

class Registry {
   private static $_storage;
   public static function register($name, $value) {
      self::$_storage[$name] = $value;
   }
   public static function registry($name) {
      return self::$_storage[$name];
   }
}

Registry::register('db', new DB());
Registry::register('log', new Log());

function doSomethingUsingRegistry() {
   Registry::registry('log')->msg('Entering function');
   Registry::registry('db')->query('INSERT INTO `table` SET `a`="b"');
   Registry::registry('log')->msg('Leaving function');
}
could be replaced with:

Code: Select all

$registry = array();
$registry['db'] = new DB();
$registry['log'] = new Log();

function doSomethingUsingRegistry() {
   global $registry;
   $registry['log']->msg('Entering function');
   $registry['db']->query('INSERT INTO `table` SET `a`="b"');
   $registry['log']->msg('Leaving function');
}
The similar could be done to 'registries as arguments'.

So, why use more complex class-based approach? Actually I know (one of) the answer(s), but I would like to hear your opinions ;)
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post by jmut »

Weirdan wrote:Btw, why would one prefer usage of generic registry over usage of simple associative array? I mean, registries are usually come in two flavors, singleton (or static) registries and registries that passed as an argument.

Static registry:

Code: Select all

class Registry {
   private static $_storage;
   public static function register($name, $value) {
      self::$_storage[$name] = $value;
   }
   public static function registry($name) {
      return self::$_storage[$name];
   }
}

Registry::register('db', new DB());
Registry::register('log', new Log());

function doSomethingUsingRegistry() {
   Registry::registry('log')->msg('Entering function');
   Registry::registry('db')->query('INSERT INTO `table` SET `a`="b"');
   Registry::registry('log')->msg('Leaving function');
}
could be replaced with:

Code: Select all

$registry = array();
$registry['db'] = new DB();
$registry['log'] = new Log();

function doSomethingUsingRegistry() {
   global $registry;
   $registry['log']->msg('Entering function');
   $registry['db']->query('INSERT INTO `table` SET `a`="b"');
   $registry['log']->msg('Leaving function');
}
The similar could be done to 'registries as arguments'.

So, why use more complex class-based approach? Actually I know (one of) the answer(s), but I would like to hear your opinions ;)
IMHO, the main reason you would use classes at all. It gives you more control (you can not only register...but also do some other stuff using register()).
You do register stuff (only that and nothing else)...this prevents you from doing all kinds of other stuff that you can with arrays e.g. (remove index..etc etc.)
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

I misinterpreted you then... I do get argumentative at times :). Especially at 2:30am.

I just don't see the point of additional wrapping. It's more code to manage, maintain and debug.
Btw, why would one prefer usage of generic registry over usage of simple associative array? I mean, registries are usually come in two flavors, singleton (or static) registries and registries that passed as an argument.
The Registry is a pattern, not an implementation. The concept is more important than how many ways (more than the three you mentioned) it can be implemented. Developer's always have preferences, and specific implementations often depend on the specific application, unit testing requirements, etc. A static for example requires a class know the Registry classname - and not just the generic interface. I don't like statics on that basis (I avoid them like the plague where possible in fact) and so I prefer the parameter approach, esp. since I use Controllers which all get identical parameters setup in one single class where it's easily managed. The Zend Framework's Zend::registry() static is being moved to a standalone Registry class which I hope will allow non-static access...

Prepending "Registry::registry('log')->" to method calls adds more typing. I'm a lazy sod. It's duplication.
global $registry;
...is the devil.

A simple Registry really is just a glorified array ;). Unfortunately an array does not allow much logic beyong the simple - hence a class approach is more flexible. I could just be spouting OOP rhetoric - if an array fits your needs, PHP5 allows imposition of a class without breaking the array interface if a need for a class arises in the future...
So, why use more complex class-based approach? Actually I know (one of) the answer(s), but I would like to hear your opinions
Unit Testing, ServiceLocator functionality added to a Registry while maintaining the interface, future considerations, use of factories, SPL, the list continues... Arrays don't cut it outside a simple scenario.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

The Registry is a pattern, not an implementation.
Agreed. To me, using plain vanilla array fits the pattern perfectly.
global $registry;
...is the devil.
It isn't a devil more than using static for that matter.
I could just be spouting OOP rhetoric - if an array fits your needs, PHP5 allows imposition of a class without breaking the array interface if a need for a class arises in the future...
Though currently it does not allow multidimensional ArrayAccess as far as I can tell.
Arrays don't cut it outside a simple scenario.
you contradict yourself, actually. Array could be replaced with class implementing ArrayAccess if the need arises.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Aha, but a class implementing ArrayAccess is not a native Array per se...:). "Native Array" being the correction which removes the contradiction...
Post Reply