Hi…
All of the following is predicated upon you having a working system on your development box. Do everything you can to achieve this first. If you have a test suite, all well and dandy. Otherwise it will be a manually refreshing a bunch of browser tabs.
Taking your only example code so far:
$img = new HImg($src);
$table = new HTable();
$table->AddRow($img);
You could start with this…
class H {
function img($src) { return new HImg($src); }
function table() { return new HTable(); }
}
This is the factory.
Then the code above becomes…
$html = new H();
$img = $html->img($src);
$table = $html->table();
$table->AddRow($img);
That’s a simple mechanical step. You probably only have to test one of the pages affected to know it works. Check it’s working of course, in case of typos.
Assuming the code above exists in SomeClass, we really have:
class SomeClass {
function __construct($stuff) { ... }
function someFunction($src) {
$html = new H();
$img = $html->img($src);
$table = $html->table();
$table->AddRow($img);
...
}
}
It’s a mechanical step again to get to this…
class SomeClass {
function __construct($stuff) {
$this->html = new H();
}
function someFunction($src) {
$img = $this->html->img($src);
$table = $this->html->table();
$table->AddRow($img);
...
}
}
Now the fiddly bit. We want to get to this…
class SomeClass {
function __construct($html, $stuff) {
$this->html = $html;
}
function someFunction($src) {
$img = $this->html->img($src);
$table = $this->html->table();
$table->AddRow($img);
...
}
}
The good news is that this will fatal if we get it wrong, so it’s easy to spot errors. The class that created us, must now instantiate H. It’s a grep job, sadly.
Anyway, it’s this step which makes it an AbstractFactory. The class using the set of elements has no idea that a HTag was ever involved.
Now another mechanical step. Push the instantiation of H up the class hiearchy one step at a time. That is, instead of instantiating it in the instantiator of SomeClass, instantiate it in the class that instantiates the instantiator of SomeClass, again passing it in the constructor.
When you’ve gone high enough you should eventually have the instantiation in just one place, or you’ve reached a bunch of index.php files. The aim is just one place. If you have it in a bunch of index files, the just write a function called html_renderer or something…
function html_renderer() {
return new H();
}
…of course.
All this is pretty boring, but it shouldn’t be possible to screw it up and you can pick it up and put it down depending on how busy you are.
Once this job is 100% complete, you can now swap it for something other than H.
Here is the nice bit. When you write your new version, you only have to test it against H. You don’t have to test the whole application (yay!). You could have a test page that generates every element or something. Render the page with H, then render it with your new version, then use diff for example. No application involved.
I’m glossing over something very important - the library you want to switch in could have a very different interface. Anything involving poorly understood code means going in small steps of course, but that should now be easy.
Lets suppose that $table->AddRow() does not exist in the new library. We can either write an adapter…
class MyTableLooksLikesHTable {
private $table;
function __construct($my_table) {
$this->table = $my_table;
}
function AddRow($img) {
$this->table->setRow($img);
}
function __call($method, $arguments) {
return call_user_func_array(array($this->table, $method), $arguments);
}
}
class H {
...
function table() {
return new MyTableLooksLikesHTable(new MyTable());
}
}
…or if that’s not possible, then group the methods into the original factory…
class H {
...
function tableWithImages($images) {
$table = new HTable();
foreach ($images as $image) {
$table->AddRow($image);
}
return $table;
}
}
Your original code snippet becomes…
class SomeClass {
function __construct($html, $stuff) {
$this->html = $html;
}
function someFunction($src) {
$table = $this->html->tableWithImages(array($this->html->img($src)));
...
}
}
Now you can implement tableWithImages() on your new factory in an entirely different way if needed.
You get another worked example in the book “Design Patterns” by Gamma, Helm, Johnson, Vlissides. Also check out Refactoring by Martin Fowler and Refactoring to Patterns by Joshua Joshua Kerievsky.
http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=sr_1_1?ie=UTF8&qid=1288703928&sr=8-1
http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672/ref=sr_1_1?ie=UTF8&qid=1288703823&sr=8-1
http://www.amazon.com/Refactoring-Patterns-Joshua-Kerievsky/dp/0321213351/ref=sr_1_2?ie=UTF8&qid=1288703823&sr=8-2
The main tips though are (a) isolate the problem first and (b) proceed in small steps. The big divide and conquer wins will drop out form that.
yours, Marcus