While I like Jason's answer, it is not, strictly speaking OO.
$question = new Question($id);
$comments = $question->getComments();
$answers = $question->getAnswers();
echo $question->getTitle();
echo $question->getText();
foreach ($comments as $comment)
echo $comments->getText();
The problems are:
- There is no information hiding, a fundamental principle of OO.
- If the format of the answers needs to change, it must be changed in a place that is not associated with the object that houses the data.
- The solution is not extensible. (There is no behaviour to inherit.)
You must keep behaviour (tightly coupled) with the data. Otherwise you are not writing OO.
$question = new Question($id);
$questionView = new QuestionView( $question );
$questionView->displayComments();
$questionView->displayAnswers();
How the information is displayed is now an implementation detail, and reusable.
Notice how this opens up the following possibility:
$question = new Question( $id );
$questionView = new QuestionView( $question );
$questionView->setPrinterFriendly();
$questionView->displayComments();
$questionView->displayAnswers();
The idea is that now you can change how the questions are formatted from a single location in the code base. You can support multiple formats for the comments and answers without the calling code (a) ever knowing; and (b) ever needing to change (to a significant degree).
If you are coding text formatting details in more than one location because you are misusing accessor methods, the life of any future maintainers will be miserable. If the maintainer is a psychopath who knows where you live, you will be in trouble.
Objects, Data, and Views
Here's the problem, as I understand it:
Database -> Object -> Display Content
You want to keep the behaviour of the object centred around logic that is intrinsic to the object. In other words, you don't want the Object to have to do things that have nothing to do with its core responsibilities. Most commonly this will include load, save, and print functionality. You want to keep these separate from the object itself because if you ever have to change database, or output format, you want to make as few changes in the system as possible, and restrain the ripple effect.
To simplify this, let's take a look at loading only Comments
; everything is applicable to Questions
and Answers
as well.
Comment Class
The Comment class might offer the following behaviours:
- Reply
- Delete
- Update (requires permission)
- Restore (from a delete)
- etc.
CommentDB Class
We can create a CommentDB
object that knows how to manipulate the Comment
s in the database. A CommentDB
object has the following behaviours:
- Create
- Load
- Save
- Update
- Delete
- Restore
Notice that these behaviours will likely be common across all objects and can therefore be subject to refactoring. This will also let you change databases quite easily as the connection information will be isolated to a single class (the grandfather of all database objects).
Example usage:
$commentDb = new CommentDB();
$comment = $commentDb->create();
Later:
$comment->update( "new text" );
Notice that there are a number of possible ways to implement this, but you can always do so without violating encapsulation and information hiding.
CommentView Class
Lastly, the CommentView
class will be tightly coupled to a Comment
class. That it can obtain the attributes of Comment
class via accessors is expected. The information is still hidden from the rest of the system. The Comment
and its CommentView
are tightly coupled. The idea is that the formatting is kept in a single place, not scattered throughout classes that need to use the data willy nilly.
Any classes that need to display comments, but in a slightly different format, can inherit from CommentView
.
See also: Allen Holub wrote "You should never use get/set functions", is he correct?