Page 1 of 1
God Object Singleton...
Posted: Sat Mar 03, 2007 8:57 pm
by Ambush Commander
Good evening, folks. I have just implemented a God Object Singleton.
Code: Select all
<?php
/**
* Super singleton object that does everything. Used for unit testing.
*/
class XHTMLCompiler
{
/** Private instance of singleton */
private static $_instance;
/** Private constructor, prevents other people from making it */
private function __construct() {}
/** Retrieves the single instance of the object */
static public function getInstance() {
if(is_null(self::$_instance)) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Overloads the instance with another one, usually a mock object
* @param Object substitute for XHTMLCompiler
*/
static public function setInstance($stub) {
// unit testing only!
self::$_instance = $stub;
}
/** Aborts script execution, equivalent to exit */
public function quit() {exit;}
/** Sends an HTTP header, equivalent to header() */
public function header() {
$args = func_get_args();
call_user_func_array('header', $args);
}
/** Outputs text, equivalent to echo */
public function paint($text) {echo $text;}
/** Retrieves the relative URI with which the page was requested. */
public function getRequestURI() {return $_SERVER['REQUEST_URI'];}
/** Retrieves the relative URI which denotes the frontend PHP file */
public function getPHPSelf() {return $_SERVER['PHP_SELF'];}
}
?>
Is this a good idea? The idea is that this gives me an object that's easy to mock, so I can unit test functions that normally would have a very concrete effect, such as send headers, or quit execution. There's always a bit of functionality here, and a bit there, that if I went all out object-frenzy I'd have some very airy files: this lets me keep things compact. Hmm...
Posted: Sun Mar 04, 2007 8:52 am
by feyd
To keep things centralized, I can't blame you. It seems fine. Extrapolated a bit, one could see it implementing a few interfaces, possibly even extending a class.
Posted: Sun Mar 04, 2007 10:58 am
by Ollie Saunders
setInstance() seems like a cool idea. Isn't a factory class typically what you would use to solve problems like this though?
All this...
Code: Select all
class XHTMLCompiler
{
/** Private instance of singleton */
private static $_instance;
/** Private constructor, prevents other people from making it */
private function __construct() {}
/** Retrieves the single instance of the object */
static public function getInstance() {
if(is_null(self::$_instance)) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Overloads the instance with another one, usually a mock object
* @param Object substitute for XHTMLCompiler
*/
static public function setInstance($stub) {
// unit testing only!
self::$_instance = $stub;
}
could quite happily be an abstract class called Singleton.
Posted: Sun Mar 04, 2007 12:10 pm
by Ambush Commander
To keep things centralized, I can't blame you. It seems fine. Extrapolated a bit, one could see it implementing a few interfaces, possibly even extending a class.
Yay, if Feyd says it's okay,

I guess I can refactor later if the class gets too big... I'll set the limit at 20 methods.
could quite happily be an abstract class called Singleton.
Yep, except that it's not used by anyone else. Therefore, I'm not so worried about the lack of inheritance. If it becomes necessary, I'll refactor.
Isn't a factory class typically what you would use to solve problems like this though?
You're right. The more appropriate term for this class is a Strategy with a Prototype factory method. But maybe I'll add some global data to it. Then it's a Singleton.
Posted: Sun Mar 04, 2007 2:49 pm
by Christopher
No ... bad idea. Slapping the functionality of the request, response and view objects into a single object ... and then patting yourself on the back for making a pseudo-testable Singleton ... urgh. If you application is really that simple, then why to you need even this. If it isn't then put request, response and view objects into a registry and pass it through. Separate concerns are still separate concerns.
Posted: Sun Mar 04, 2007 2:57 pm
by Ambush Commander
If you application is really that simple, then why to you need even this.
So I can test stuff like:
Code: Select all
function normalize_index($page, $directory_index) {
$xc = XHTMLCompiler::getInstance();
if ($page == '') $page = $directory_index;
if ($xc->isDir($page)) {
if ($page[strlen($page)-1] !== '/') $page .= '/';
$page .= $directory_index;
}
return $page;
}
It's simple, yes, but I have no interest in implementing a full "FileSystem" object hierarchy, so the ability to overload is_dir() with a mock is appreciated.
What would you recommend? My main concern with separating concerns is that it leads to a level of abstraction that is unnecessary for something as simple as this. There are fifteen functions in the entire application, dealing with Response, Request, Filenames, Directory Globbing, Cache Management, and HTML processing.
Posted: Sun Mar 04, 2007 3:08 pm
by Christopher
Ambush Commander wrote:What would you recommend? My main concern with separating concerns is that it leads to a level of abstraction that is unnecessary for something as simple as this. There are fifteen functions in the entire application, dealing with Response, Request, Filenames, Directory Globbing, Cache Management, and HTML processing.
I would recommend creating a library of helper functions. None of those function have much to do with each other, they just wrap PHP library calls. To me, calling it
XHTMLCompiler_isdir() is much more honest.
Posted: Sun Mar 04, 2007 3:12 pm
by Ambush Commander
If given the choice, I'd use the real functions, but then it's a PITA to mock them. With the god object, I can do this:
Code: Select all
function test_normalize_index_File() {
$this->mock->expectOnce('isDir', array('main.html'));
$this->mock->setReturnValue('isDir', false);
$this->assertEqual(normalize_index('main.html', 'index.html'), 'main.html');
}
I wasn't originally planning on unit testing this application, but when I tried to do a function renaming I realized how running blind I was. The unit tests, even if just for code coverage, help me cohesively test the entire app in one go.
Posted: Sun Mar 04, 2007 3:23 pm
by Christopher
I think I am not clear what is being tested and what is supporting testing?
Posted: Sun Mar 04, 2007 3:30 pm
by Ambush Commander
Alright. Here's the setup.
- We have several PHP files that serve as entry points into the application. Not unlike page controllers.
- These pages use helper functions to do duplicated logic. These functions are what we want to test.
- The helper functions call lots of internal PHP functions. Safe ones are like strpos and substr, unsafe ones are like header and scandir.
I need some way to test helper functions that use unsafe internal functions. So, instead of calling unsafe functions directly, I pass them through my god class wrapper, which acts purely as a facade, and implements no logic. When I need to test, I replace the god class with a server stub, so I can carefully control the testing environment.
Ideally speaking, we would have a much finer granularity of objects to facilitate server stubs. However, I've been balking at implementing objects, because many of them would be effectively one or two methods and no data.