TalkPHP

TalkPHP (http://www.talkphp.com/forums.php)
-   Advanced PHP Programming (http://www.talkphp.com/advanced-php-programming/)
-   -   Interfaces Revisited (http://www.talkphp.com/advanced-php-programming/1468-interfaces-revisited.html)

Karl 11-15-2007 12:19 PM

Interfaces Revisited
 
After my last article on interfaces a few people said it would have been clearer if I had given a better example. I decided to write this tutorial/article to further explain the use of interfaces (and to give a decent example).

The code in this article will solve the following real life problem that I recently had to solve: I needed a system that allowed me to send emails to different combinations of groups of users.

There were many different variations of "user groups" and so I needed a flexible system to allow me to easily send emails to different combinations of these groups and to also allow me to easily add, delete and change groups at a later date.

I decided I needed one core class (which I named GroupEmailer) and one interface (which I namded RecipientGroup). I will later create classes that implement that interface.

The class, GroupEmailer, is responsible for sending a specific email to a group of recipients. It must know which group of users we want to send emails to. I've left out the implementation of sending the actual email as I don't feel it is important to the example.

PHP Code:

class GroupEmailer
{
    private 
$m_aGroups;
    
    public function 
addGroup(RecipientGroup $pGroup)
    {
        
$this->m_aGroups[] = $pGroup;
    }
    
    public function 
send()
    {
        foreach (
$this->m_aGroups as $pGroup)
        {
            
$aRecipients $pGroup->getRecipients();
            
            foreach (
$aRecipients as $szRecipient)
            {
                echo 
$szRecipient '<br />';
            }
        }
    }


If you read the previous article on interfaces this should look very similar to the PagesController class and should therefore need little explanation. With that said, you should note that in the send() method I've simply looped through and printed out each recipient, in a real life situation we'd obviously be sending out emails instead of printing out the list of recipient addresses. In case you're wondering how you could do this, you could simply pass in the email details through the send() method, such as send('Subject', 'Message body').

You can probably imagine how this will work. We'll add "groups" to the class (using addGroup()) which represents the group of users that we want to email, then we'll call the send() method to send the emails to each member of the groups.

However, we've not actually got any code for returning the groups yet. You may have noticed that the addGroup() method is expecting an object that implements the RecipientGroup interface, this means that we need to create some classes that implement this interface. Before we do that, we need to create our interface:

PHP Code:

interface RecipientGroup
{
    public function 
getRecipients();


We can determine that this interface needs only one method because each recipient group has only one responsibility: to return the list of recipient email addresses that belong to the group that it represents, for example, an Admin group would be responsible for returning a list of email addresses for all admins of the system.

Now that we have an interface to design our groups to we can start creating our group classes. We'll create two classes for this example, one for retrieving a list of admin email addresses and one for retrieving a list of member email addresses.

Here's the class for retrieving the list of admin email addresses:

PHP Code:

class RecipientGroup_Admins implements RecipientGroup 
{
    public function 
getRecipients()
    {
        
// fetch list of admin emails from db
        
return array('karl@talkphp.com''wildhoney@talkphp.com'
                
'salathe@talkphp.com''bluesaga@talkphp.com');
    }


Notice that we're not actually fetching any emails from anywhere, I've just hard-coded them so you can run the example and actually see how this would work before implementing it.

Here's the class for retrieving the list of member email addresses. This is almost exactly the same as the previous class

PHP Code:

class RecipientGroup_Members implements RecipientGroup 
{
    public function 
getRecipients()
    {
        
// fetch list of member emails from db
        
return array('john@hotmail.com''peter@yahoo.com'
                
'paul@gmail.com''bob@aol.com');
    }


Now that we have our two recipient groups we can give this a try:

PHP Code:

// Send an email to admins only 
$pGroupEmailer = new GroupEmailer();
$pGroupEmailer->addGroup(new RecipientGroup_Admins());
$pGroupEmailer->send(); 

This would output the following:

Code:

karl@talkphp.com
wildhoney@talkphp.com
salathe@talkphp.com
bluesaga@talkphp.com

We can easily send to an additional group of users by adding that to the GroupEmailer

PHP Code:

// Let's send an email to admins and members
$pGroupEmailer = new GroupEmailer();
$pGroupEmailer->addGroup(new RecipientGroup_Admins());
$pGroupEmailer->addGroup(new RecipientGroup_Members());
$pGroupEmailer->send(); 

Running this code would send emails to both groups, resulting in:

Code:

karl@talkphp.com
wildhoney@talkphp.com
salathe@talkphp.com
bluesaga@talkphp.com
john@hotmail.com
peter@yahoo.com
paul@gmail.com
bob@aol.com

We've now got a flexible system for sending emails to groups of members. We could easily add more groups by creating more "group" classes. How you go about collecting the email addresses (for example, from a database or hard coded) is of no concern to EmailSender, this gives you the flexibility to add, delete and change groups as your project requirements change.

bluesaga 11-15-2007 01:42 PM

Really nice and clean mate, easy to read and definitely helps get a grasp on interfaces!

Can you think of another reason to use interfaces alike you have? I'm finding it hard to think of a good use for something similar to the:

PHP Code:

$pGroupEmailer->addGroup(new RecipientGroup_Admins());
$pGroupEmailer->addGroup(new RecipientGroup_Members()); 

Part, where you use multiple classes. Using only one would be easy, databases or something, but I cant think of a use for using two classes alike that....

Wildhoney 11-15-2007 01:47 PM

In a nutshell it saves you having to go back to modify a class. Instead you can create another class and then include it in the construct's argument. Keep in mind that although you have a little repetition of code, all that should really be a repetition is the way you handle the MySQL queries, though this should as well be used via a MySQL class which makes querying simple.

Karl 11-15-2007 02:21 PM

bluesaga, another good example of this pattern can be found in the Zend Framework (in fact, they've used this kind of pattern throughout). If you look at the Zend_Valdiate class you can see that it can be used just like the class I built, it allows you to mix and match different validations to create your own valdiation group, here's some example code from the Zend Framework Reference Manual.

PHP Code:

// Create a validator chain and add validators to it
$validatorChain = new Zend_Validate();
$validatorChain->addValidator(new Zend_Validate_StringLength(612))
               ->
addValidator(new Zend_Validate_Alnum()); 


bluesaga 11-15-2007 02:32 PM

Ah right, yea thats a great example! I see now, i take it addValidator makes an array of the classes, and then "doValidate" (or something similar) loops through those, checking for a exception and returning the exception if it finds one?

I can see exactly why that works, and how its a great thing :)

One thing though, what happens if say you have 20-30 different string to validate. Surely this method would be a little slow? (Need different instances of Zend_Validate for each different string format?)

Karl 11-15-2007 02:40 PM

yes it would be a little slow. Unfortunately you have to give up some efficiency for the flexibility of such a system. The same can be said with a lot of agile design patterns, they allow far superior flexibility but unfortunately you've got to model a lot of the data using objects, which can hinder the performance of your system :(

With that said, there are many ways to improve the performance of your PHP applications. I dont just mean small performance boosts either, you can increase performance over 50% by just using a compiler cache.


All times are GMT. The time now is 01:11 AM.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.1.0