| home / programming / phpanth2 / 1 | [previous] |
|
|
There are two ways in which one object can use another: aggregation and composition.
A common example of aggregation in action involves a database connection class. Imagine you pass a database connection class to some other class, which then uses the database connection class to perform a query. The class performing the query aggregates the database connection class.
Here’s a simple example using the MySQL class, which we’ll create in Chapter 3, PHP and MySQL :
<?php
// Include the MySQL database connection class
require_once 'Database/MySQL.php';
// A class which aggregates the MySQL class
class Articles {
var $db;
var $result;
// Accept an instance of the MySQL class
function Articles(&$db)
{
// Assign the object to a local member variable
$this->db = &$db;
$this->readArticles();
}
function readArticles()
{
// Perform a query using the MySQL class
$sql = "SELECT * FROM articles LIMIT 0,5";
$this->result = &$this->db->query($sql);
}
function fetch()
{
return $this->result->fetch();
}
}
// Create an instance of the MySQL class
$db = &new MySQL('localhost', 'harryf', 'secret', 'sitepoint');
// Create an instance of the Article class, passing it the MySQL
// object
$articles = &new Articles($db);
while ($row = $articles->fetch()) {
echo '<pre>';
print_r($row);
echo '</pre>';
}
?>
In the above example, we instantiate the MySQL class outside the Articles class, then pass it to the Articles constructor as Articles is instantiated. Articles is then able to use the MySQL object to perform a specific query. In this case, Articles aggregates the MySQL object. Figure 2.2 illustrates this relationship with UML.
One example from Chapter 1, Access Control is the Auth class, which composes an instance of the Session class, creating it in the constructor:
class Auth {
…
/**
* Instance of Session class
* @var Session
*/
var $session;
…
function Auth (&$dbConn, $redirect, $md5 = true)
{
$this->dbConn = &$dbConn;
$this->redirect = $redirect;
$this->md5 = $md5;
$this->session = &new Session();
$this->checkAddress();
$this->login();
}
Because the Auth class needs to read and write to session variables, and only a limited number of other, unrelated classes in an application are likely also to need to use Session, it’s logical that it gets to create its own Session object.
Figure 2.3 illustrates the composition in this example with UML.
In terms of practical development, knowing when to apply aggregation or composition is important.
Aggregation has the advantage of lower overhead, because a single object will be shared by many other objects. Certainly, aggregating your database connection class is a good idea; composing it with every object that wants to make a query may require you to have multiple connections to your database, which will quickly halt your application when your site attracts high levels of traffic.
Composition has the advantage of making classes easier to work with from the outside. The code that uses the class doesn’t have to worry about passing it the other objects it needs, which, in a complex application, can often become tricky and result in a design “work around.” Composition also has the advantage that you know exactly which class has access to the composed object. With aggregation, another object sharing the aggregated object may do something to its state that “breaks” the object as far as the other classes that use it are concerned.
An interface is one or more methods that let you use a class for a particular purpose. For example, you could have two database connection classes—one for MySQL and one for PostgreSQL. As long as they both offered a query method, you could use them interchangeably for running queries on different databases. The query method is a simple interface that the two classes share.
The classes sharing the same interface are often inherited from a parent class that makes the common methods available. Again, this is best understood by example.
First, we define an abstract base class, Message, which provides the common method getMessage. Beneath the Message class, we define concrete classes, each of which creates a specific message.
The terms “abstract” and “concrete” refer to class usage, in particular, whether a class is intended to be used directly or not. An abstract class is one in which some functionality or structure is to be shared by all subclasses, but is not intended to be used directly; typically, it has one or more empty methods that don’t do anything useful. In other words, you’re not supposed to create objects from an abstract class. A concrete class is a subclass of the abstract class from which you can create objects. Some languages, like Java, provide support for abstract classes within the language syntax—something PHP 4 doesn’t offer. You can still use the concept of abstract classes when designing applications, though you might consider adding documentation to tell other developers working with the code that the class is abstract.
<?php
class Message {
var $message;
function setMessage($message)
{
$this->message = $message;
}
function getMessage()
{
return $this->message;
}
}
class PoliteMessage extends Message {
function PoliteMessage()
{
$this->setMessage('How are you today?');
}
}
class TerseMessage extends Message {
function TerseMessage()
{
$this->setMessage('Howzit?');
}
}
class RudeMessage extends Message {
function RudeMessage()
{
$this->setMessage('You look like *%&* today!');
}
}
Now, we define the MessageReader class, which takes an array of Message objects through its constructor.
class MessageReader {
var $messages;
function MessageReader(&$messages) {
$this->messages = &$messages;
$this->readMessages();
}
function readMessages() {
foreach ($this->messages as $message) {
echo $message->getMessage() . '<br />';
}
}
}
The important thing to note here is that, as far as MessageReader is concerned, a “Message object” is any object that was instantiated from the Message class or one of its subclasses . Did you see how, inside the readMessages method, we call the getMessage method? This code will work on any object that has a getMessage method—including any subclass of Message.
Now, to prove the point, let’s create some Message objects using our three subclasses at random:
Example 2.26. 14.php (excerpt)
$classNames =
array('PoliteMessage', 'TerseMessage', 'RudeMessage');
$messages = array();
srand((float)microtime() * 1000000); // Prepares random shuffle
for ($i = 0; $i < 10; $i++) {
shuffle($classNames);
$messages[] = new $classNames[0]();
}
$messageReader = new MessageReader($messages);
?>
By creating the array $classNames and then repeatedly shuffling it, we can take the first element of the array and use it to create a new object:
$messages[] = new $classNames[0]();
This is an example of a variable function . The expression $classNames[0] is evaluated to determine the name of the constructor (PoliteMessage, TerseMessage, or RudeMessage) to call.
Finally, the $messages array contains ten messages, randomly selected, and is passed to the constructor of MessageReader on instantiation.
Here’s a sample result:
You look like *%&* today! Howzit? How are you today? How are you today? How are you today? You look like *%&* today! How are you today? How are you today? Howzit? How are you today?
Each time we execute the script, the list is different.
Because all the concrete message classes share the same getMethod function (i.e. they implement the same interface), the MessageReader class is able to extract the data without knowing which particular type of message it’s dealing with. The ability for a group of related classes to work interchangeably is called polymorphism , and is illustrated in the UML diagram in Figure 2.4.
This aspect of object oriented programming can be very powerful once you realize its worth. You might have a collection of objects representing HTML tags, for example, each being a subclass of a parent HTMLTag class, from which they all inherit a render method. Another class that handles the rendering of a page could take a collection of HTMLTag objects and create the page by calling each object’s render method.
Object Oriented PHP: Paging Result Sets: http://www.sitepoint.com/article/662
PHP References Explained: http://www.zez.org/article/articleview/77/
PHP References
Part 1: http://www.onlamp.com/pub/a/php/2002/08/15/php_foundations.html
Part 2: http://www.onlamp.com/pub/a/php/2002/09/12/php_foundations.html
PHP Reference Counting and Aliasing: http://www.zend.com/zend/art/ref-count.php
[1] Procedural programming is the name given to non-object oriented programming. All the code we’ve seen in this book so far has been procedural in nature.
[2] Refactoring is the process of restructuring code without actually changing what it does. This is usually done to ease future maintenance and expansion of the code that would be hindered by its current structure.
[3] Enforced privacy constraints on class members will be added in PHP 5.0.
| home / programming / phpanth2 / 1 | [previous] |
Created: March 11, 2003
Revised: January 2, 2004
URL: http://webreference.com/programming/phpanth2