I have a Display
object that handles HTML output of the script. It has a mode
property, which defines many aspects of how the output is generated. There are several modes, and I will probably add more later. Each mode implies it's own parameters. For example, an author
mode would imply an authorID
. A search
mode would imply a Search
object as a parameter, which would contain query information. A comment
mode would imply a postID
and commentID
parameters.
So, a mode
has a name
and some number of parameters
, depending on the name
.
I decided to create a Display_Mode
object, which has two properties: name
and parameters
. But how do I assign parameters
to names
? So that if I have a comment
mode, it would neccessarily mean that there are postID
and commentID
parameters?
My current solution is this:
class display_mode{
public $name;
public $params;
public function __construct($name)
{
$this->name = $name;
switch ($this->name){
case 'comment':
$this->params = array('postID', `commentID`);
break;
case 'author':
$this->params = array('authorID');
}
//etc..
}
}
This seems a bit messy. Is there a better way?
UPD: given answers led me to ask another question, concerning the whole design, which also provides context for this one.
You're better off abstracting the common functionality of the Display_Mode
class into an abstract base class or interface and then defining each individual mode as a class that inherits from the base class (or implements the interface).
These specialised mode classes would then explicitly define the parameters they require in their constructors and/or methods, and would be responsible for producing the output that each "mode" requires.
It's difficult to give more specific advice than this without knowing exactly what your Display_Mode
class is supposed to do, however.
The idea here is that you should avoid do-it-all classes in OOP, preferring small, self-contained classes each with a single purpose. By giving each mode its own class that can independently render its content, you're making it easy to change the way display modes work or add new ones. All you have to do is add a new class.
This is known as loose coupling.
The description is kinda confusing, but I would create an adapter for each possible "parameter" of Display
. This adapter could provide single interface, independently of the the resource that you want to display.
$parameter = new UnknownPrameter;
$adapterFactory = new AdapterFactory;
$parameter = $adapterFactory->build( $parameter );
$display->present( $parameter );
Where AdapterFactory::build()
create a wrapper for the specific type of parameter, that you supplied. The returned instance is a container for that parameter.
This approach would also prevent the computation from accumulating in the constructor, which would make the code harder to test/expand.
@Will Vousden already gave you the answer. This is a quick example of how to approach your problem.
abstract class DisplayMode {
protected $_params = array();
public function __construct(array $params) {
$this->_params = $params;
}
public function hasParameter($key) {
if (array_key_exists($key, $this->_params)) {
return true;
}
return false;
}
public function setParameters(array $params) {
$this->_params = $params;
return $this;
}
public function getParameters() {
return $this->_params;
}
}
class CommentMode extends DisplayMode {
public function getCommentId() {
if ($this->hasParameter('comment_id')) {
return $this->_params['comment_id'];
}
return null;
}
public function getPostId() {
if ($this->hasParameter('post_id')) {
return $this->_params['post_id'];
}
return null;
}
}
class AuthorMode extends DisplayMode {
public function getAuthorId() {
if ($this->hasParameter('author_id')) {
return $this->_params['author_id'];
}
return null;
}
}
$comment = new CommentMode(array('post_id' => 4, 'comment_id' => 2));
$author = new AuthorMode(array('author_id' => 3));
// example
print $comment->getCommentId() . ' - ' . $comment->getPostId() . ' - ' . $author->getAuthorId();