I'm just reading a book called "Holub on Patterns" (Java based code examples but I think the theory he talks about still applies).
Chapter 2: "Programming with Interfaces and a Few Creational Patterns"
Section: "The Fragile-Base-Class Problem"
He argues that by extending a base class it becomes fragile because other derived implementations become obscured as programmers extend the interface defined by the base class. Whereas interfaces do not obscure the interface with implementation.
I don't think I'm communicating his message very well, perhaps an example from the book will help.
(The code here has been interpreted from a Java based example.)
PHP Code:
class ArrayList
{
// class implementation
function add() { /*... */ }
function remove() { /*... */ }
function clear() { /*... */ }
}
class Stack extends ArrayList
{
private topOfStack = 0;
public push($article)
{
$this->add($this->topOfStack++, $article);
}
public pop()
{
$this->remove(--$this->topOfStack);
}
public pushMany($articles)
{
foreach ($articles as $article) {
$this->push($article);
}
}
}
$stack = new Stack();
$stack->push("1");
$stack->push("2");
$stack->clear();
$stack->push("3");
...since the base class doesn't know anything about the index of the item at the top of the stack (topOfStack), the Stack object is now in an undefined state. The next call to push() puts the new item at index 2 (the current value of the topOfStack), so the stack effectively has three elements on it, the bottom two of which are garbage.
One (hideously bad) solution to the inheriting-undesirable-methods problem is for Stack to override all the methods of ArrayList that can modify the state of the array to manipulate the stack pointer. This is a lot of work, though, and doesn't handle problems such as adding a method like clear() to the base class after you've written the derived class.
Quote by Allen Holub from "Holub on Patterns" (page 41 to 42).
He goes onto to say that the programmer shouldn't exclude the use of base classes, which are very powerful, but carefully consider their use. They can be extremely useful to share functionality but can also cause code like the example described above. He then describes how interfaces negate this and other problems at the cost of losing the shared implementation from the base class.
[edit] Here's a suggested solution using interfaces
PHP Code:
interface Stack
{
public function push($article);
public function pop();
public function pushMany($articles);
public function size();
}
class SimpleStack implements Stack
{
private $topOfStack;
private $arrayStack;
function __construct()
{
$this->arrayStack = new ArrayStack();
}
function push($article)
{
$this->arrayStack->add($this->topOfStack++, $article);
}
// you can work out the rest...
}
Hope that helps someone.
Luke
Bookmarks