Alternative to Registry: Static functions

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
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Alternative to Registry: Static functions

Post by Ambush Commander »

This is a question related to the AuthTools project, but it's related to pretty much all toolkits out there, so I'm throwing it out to the general community (not that the other topic isn't open to the whole community, but it can be hard to follow at times, heh).

I don't really want to use a registry or globals, but I also don't want to force the client to implement a singleton. What I think is better is a way to get a concrete class from an abstract interface based on a prototype the client passed a static function earlier. Confused? It's quite simple.

Instead of:

Code: Select all

$r = new MersenneTwister();
$registry = Registry::instance();
$registry->setRandomNumberGenerator($r);
// ...
$registry = Registry::instance();
$random = $registry->getRandomNumberGenerator();
we do this:

Code: Select all

$r = new MersenneTwister();
RandomNumberGenerator::setDefault($r);
// ...
$random = RandomNumberGenerator::getDefault();
The backend could even be implemented as a registry, but to me, it makes more sense to tie a default class to the interface/abstract class's static function (really a factory method) rather than to some random registry. Ideally speaking, I wouldn't have to implement a registry for this toolset at all. Furthermore, as long as clone() method is implemented, $random need not be an instance of exactly the same class (although for this case, this would make sense).

This however means that compatibility gunk will be added to lots and lots of classes. Is it a good idea?
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I tend to do the latter over the former.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I tend to do the former over the latter.

The curious statement to me is:
Ambush Commander wrote:I don't really want to use a registry or globals, but I also don't want to force the client to implement a singleton.
(#10850)
Roja
Tutorials Group
Posts: 2692
Joined: Sun Jan 04, 2004 10:30 pm

Post by Roja »

Not to take things off-topic, but this may be a "forest for the trees" problem. Having an object and methods for grabbing a random number may be sacrificing worthwhile procedural solutions just to have OOP.

I'm interested in seeing more of the code behind the RNG, and why you need to call a function instead of just using mt_rand.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Roja wrote:Not to take things off-topic, but this may be a "forest for the trees" problem. Having an object and methods for grabbing a random number may be sacrificing worthwhile procedural solutions just to have OOP.
I had exactly the same feeling.
(#10850)
Roja
Tutorials Group
Posts: 2692
Joined: Sun Jan 04, 2004 10:30 pm

Post by Roja »

arborint wrote:
Roja wrote:Not to take things off-topic, but this may be a "forest for the trees" problem. Having an object and methods for grabbing a random number may be sacrificing worthwhile procedural solutions just to have OOP.
I had exactly the same feeling.
Man, if Arborint and I agree that things may not be right, thats a definite sign you should rethink things. :P
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Roja wrote:Man, if Arborint and I agree that things may not be right, thats a definite sign you should rethink things. :P
I thought we agreed on most things but just like to discuss! :)
(#10850)
Gambler
Forum Contributor
Posts: 246
Joined: Thu Dec 08, 2005 7:10 pm

Post by Gambler »

I would go with static. Less typing, same result. In this partucular instance you can actually use in-funtion static var to store MersenneTwister. But it will not work for all cases.
Roja
Tutorials Group
Posts: 2692
Joined: Sun Jan 04, 2004 10:30 pm

Post by Roja »

arborint wrote:
Roja wrote:Man, if Arborint and I agree that things may not be right, thats a definite sign you should rethink things. :P
I thought we agreed on most things but just like to discuss! :)
On OOP and Procedural, I think you are (much) more aggressive towards the OOP side. I don't really disagree with most of your positions (and in fact some of your posts have inspired me to some, shall we say, creative solutions?), but I generally find myself far on the other side of the spectrum.

I've mentioned before that I think its partly due to my choice in projects. I've been working in a pile of procedural mess as my favorite hobby for several years, and its influencing me strongly. At work, I deal with some fairly rigidly OOP code, but I have less flexibility (because of the job requirements, not the coding style).

Its less that we disagree, and more that we 'default' to the opposite side of the spectrum. I think we both see the value in both sides of the spectrum (a key difference from other people that used to visit here), and thats what makes the discussions entertaining and friendly, instead of something more sinister.

In this case, I was a little surprised that we both noticed that issue, and agreed that it was a concern. If anything, it made me feel ahead of my game for once. (Lately, I've felt a little off, since I've been heavily under the influence of <span style='color:red;text-decoration:blink' title='Alert a moderator!'>grilled spam</span> due to a nasty fall down the stairs).

Enough rambling. I just thought it was cool. :)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Hmm... interestingly off topic.

The reason why I'm using an object as a random number generator is because it allows for polymorphism. I'm wary about directly using PHP's functions because we have both rand() and mt_rand(), both of which tie a person to specific implementations. Supposing (and just supposing, for I here Mersenne Twister is very good) that a new, better random function comes out, I won't have to replace mt_rand with whatever this new function is called.

However, the more important point is that the user can override this if need be. If we have it directly hard-coded in, there's no way to change it without patching the toolkit. If we have indirection implemented using callbacks, well, that would work, but I'd rather not (I dislike callbacks: just use a command object).

Finally, I'm actually more interested in a random string generator. My slip. There is no in-built function for that, so some userland (in reference to PHP) implementation is necessary. I'm not precisely sure which implementation it is the best (the manual's comments offer so many options), so it's probably better if we don't force something on the user (if they don't want it).

Maybe too much flexibility? I can use this method elsewhere though, so don't get too hung up on the example.
I don't really want to use a registry or globals, but I also don't want to force the client to implement a singleton.
Hmm... not precisely sure what I meant there, I think what I meant to say was I also wanted to be able to create completely new (cloned) objects, not just references to a single one.

Let's see: Feyd and Gambler for static methods, arborint for registry. Hmm...
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Ambush Commander wrote:Let's see: Feyd and Gambler for static methods, arborint for registry. Hmm...
Based on that you should go with the static methods.

I think my question is -- where are the two ends of this code used. For example, simplest solution might a function with a static local. It could sort out what the parameter is and just deal with it. A handy helper function. If it never got a parameter it would use rand() or mt_rand().

Code: Select all

$r = new MersenneTwister();
authtools_rand_string($r);
// or
authtools_rand_string('myrandrandfunc');
$random = authtools_rand_string();
But if the user of the code provides an alternate generator object at config, but the calls to get random values are deep in the code -- then that is a different thing. That depends more on you total architecture. What you showed was just a couple of lines together.
(#10850)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

I see what you're suggesting. It could very well work nicely as a global function... but I think I'll stick with objects if I want to add an extra method or something (although I can also see how that would be implemented using a function).
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

If it applies, I think your use of the term polymorphism seemed to suggest the obvious solution. I say go with it.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Ambush Commander wrote:I see what you're suggesting. It could very well work nicely as a global function...
I wasn't suggesting it -- I was just showing the logical extension of the static direction. The function is really no different than your class design wise.
Maugrim_The_Reaper wrote:If it applies, I think your use of the term polymorphism seemed to suggest the obvious solution.
But the generator class does not need to be polymorphic and, in fact, the static eliminates that possiblity for the generator. Either solution allows the plugin to be polymorphic though.
(#10850)
User avatar
Jenk
DevNet Master
Posts: 3587
Joined: Mon Sep 19, 2005 6:24 am
Location: London

Post by Jenk »

bit of a late reply.. but I've recently stumbled upon a similar situation. I opted use a static method in nearly all of my class to retain one instance of an object instantiated from the same class (where I do not require multiple objects, however I can of course still use multiple objects where need be)

Thus, all the classes now have a method like so:

Code: Select all

<?php

class Foo
{
	public static function instance ()
	{
		static $instance;
		if (empty($instance)) {
			$instance = new Foo;
		}
		return $instance;
	}
}

?>
I don't know if this is what we know as a singleton? First time I have almost been forced to resort to using this. Anyway, I've found it very useful. Allows me to globally call the instance, for example in my view class which is my first stab at a view controller:

Code: Select all

class jmt_View
{
	private $template;
	private $smarty;
	
	public static function instance ()
	{
		static $instance;
		if (empty($instance)) {
			$instance = new jmt_View;
		}
		return $instance;
	}
	
	public function __construct ()
	{
		$this->smarty = new Smarty;
		
		$this->smarty->caching = false;
		$this->smarty->cache_dir = realpath('includes/Smarty/cache');
		$this->smarty->config_dir = realpath('includes/Smarty/configs');
		$this->smarty->compile_dir = realpath('includes/Smarty/templates_c');
		$this->smarty->template_dir = realpath('includes/Smarty/templates');
	}
	
	public function setTemplate ($template)
	{
		if ($this->smarty->template_exists($template)) {
			$this->template = $template;
		} else {
			throw new FileNotFoundException ('Template file not found: ' . $template);
		}
	}
	
	public function assignVariable ($var, $val)
	{
		$this->smarty->assign($var, $val);
	}
	
	public function displayPage ()
	{
		$this->smarty->display($this->template);
	}
}
I can simply call upon:

Code: Select all

jmt_View::instance()->setTemplate('template.tpl');
jmt_View::instance()->displayPage();
From any scope - the part that forced me to do so.
Post Reply