Wednesday, July 18, 2012

PHP Pattern Principles


Composition and Inheritance


Inheritance is a powerful way of designing for changing circumstances or contexts. It can limit flexibility, however, especially when classes take on multiple responsibilities.


The Problem


As you know, child classes inherit the methods and properties of their parents (as long as they are protected or public elements). We use this fact to design child classes that provide specialized functionality. Figure 1 presents a simple example using the UML.

Figure 1: A parent class and two child classes

The abstract Lesson class in Figure 1 models a lesson in a college. It defines abstract cost() and chargeType() methods. The diagram shows two implementing classes, FixedPriceLesson and TimedPriceLesson, which provide distinct charging mechanisms for lessons.
Using this inheritance scheme, we can switch between lesson implementations. Client code will know only that it is dealing with a Lesson object, so the details of costing will be transparent.
What happens, though, if we introduce a new set of specializations? We need to handle lectures and seminars. Because these organize enrollment and lesson notes in different ways, they require separate classes. So now we have two forces that operate upon our design. We need to handle pricing strategies and separate lectures and seminars. Figure 2 shows a brute-force solution.

Figure 2: A poor inheritance structure
Figure 2 shows a hierarchy that is clearly faulty. We can no longer use the inheritance tree to manage our pricing mechanisms without duplicating great swathes of functionality. The pricing strategies are mirrored across the Lecture and Seminar class families. At this stage, we might consider using conditional statements in the Lesson super class, removing those unfortunate duplications. Essentially, we remove the pricing logic from the inheritance tree altogether, moving it up into the super class. This is the reverse of the usual refactoring where we replace a conditional with polymorphism. Here is an amended Lesson class:
abstract class Lesson
{
   protected $duration;
   const FIXED = 1;
   const TIMED = 2;
   private $costtype;
function __construct( $duration, $costtype=1 )
{
   $this->duration = $duration;
   $this->costtype = $costtype;
}
   function cost()
   {
               switch ( $this->costtype ) {
               CASE self::TIMED :
               return (5 * $this->duration);
               break;
               CASE self::FIXED :
               return 30;
               break;
               default:
               $this->costtype = self::FIXED;
               return 30;
   }
}
   function chargeType()
   {
               switch ( $this->costtype)
               {
                           CASE self::TIMED :
                           return "hourly rate";
                           break;
                           CASE self::FIXED :
                           return "fixed rate";
                           break;
                           default:
                           $this->costtype = self::FIXED;
                           return "fixed rate";
               }
   }
// more lesson methods...
}
class Lecture extends Lesson
{
   // Lecture-specific implementations ...
}
class Seminar extends Lesson
{
   // Seminar-specific implementations ...
}
You can see the new class diagram in Figure 3.




Figure 3: Inheritance hierarchy improved by removing cost calculations from subclasses

We have made the class structure much more manageable, but at a cost. Using conditionals in this code is a retrograde step. Usually, we would try to replace a conditional statement with polymorphism. Here we have done the opposite. As you can see, this has forced us to duplicate the conditional statement across the chargeType() and cost() methods.

PHP Pattern Principles

Although design patterns simply describe solutions to problems, they tend to emphasize solutions that promote reusability and flexibility. To achieve this, they manifest some key object-oriented design principles.

This article will cover

  • Composition: How to use object aggregation to achieve greater flexibility than you could with inheritance alone
  • Decoupling: How to reduce dependency between elements in a system
  • The power of the interface: Patterns and polymorphism

The Pattern Revelation

I first started working in an object-oriented context using the Java language. As you might expect, it took a while before some concepts clicked. When it did happen, though, it happened very fast, almost with the force of revelation. The elegance of inheritance and encapsulation bowled me over. I could sense that this was a different way of defining and building systems. I “got” polymorphism, working with a type and switching implementations at runtime.

All the books on my desk at the time focused on language features and the very many APIs available to the Java programmer. Beyond a brief definition of polymorphism, there was little attempt to examine design strategies.

Language features alone do not engender object-oriented design. Although my projects fulfilled their functional requirements, the kind of design that inheritance, encapsulation, and polymorphism had seemed to offer continued to elude me.

My inheritance hierarchies grew wider and deeper as I attempted to build new classes for every eventuality. The structure of my systems made it hard to convey messages from one tier to another without giving intermediate classes too much awareness of their surroundings, binding them into the application and making them unusable in new contexts.

It wasn’t until I discovered Design Patterns, otherwise known as the Gang of Four book, that I realized I had missed an entire design dimension. By that time I had already discovered some of the core patterns for myself, but others contributed to a new way of thinking.

I discovered that I had over privileged inheritance in my designs, trying to build too much functionality into my classes. But where else can functionality go in an object-oriented system?
I found the answer in composition. Software components can be defined at runtime by combining objects in flexible relationships. The Gang of Four boiled this down into a principle: “favor composition over inheritance.” The patterns described ways in which objects could be combined at runtime to achieve a level of flexibility impossible in an inheritance tree alone.

Pearsoap-hello-server.php

In the PEAR::SOAP version of our simple SOAP service, pearsoap-hello-server.php, you'll see a quite different approach to server implementing that you've got used to walking through the two previous examples. As stated, PEAR::SOAP requires us to define a class that describes the service we want to deploy.
The SOAP_Server class will implement the SOAP service described by our class, so that we need to include:

<?
    /* Include PEAR::SOAP's SOAP_Server class: */
    require_once('SOAP/Server.php');
Here we start the definition of our class, SOAP_Hello_Server:

    /* To define a new SOAP service with PEAR::SOAP, we need to
       construct a class that defines the characteristics for our
       service. An instance of this class is then used by SOAP_Server
       to create a new SOAP service: */
    class SOAP_Hello_Server
    {

In the constructor of SOAP_Hello_Server, we define the $dispatch_map variable. This variable is used by PEAR::SOAP to figure out what methods the service should process requests to, and what kind of data is sent in and out of these methods (I.e. what parameters is passed to them, and what kind of data they return).
        /* $dispatch_map helps SOAP_Server figure out which parameters
               are used with the methods: */
        var $dispatch_map = array(); 
        
        /* dispatch mapping is optional.  It is used in non-wsdl servers to allow       for being more explicit with what arguments and types are passed in and out.*/

        /* Here's the constructor for out class. It is used to define
           $dispath_map: */
        function SOAP_Hello_Server()
        {
            $this->dispatch_map['helloWorld'] = array(
                                                        'in'    => array('inmessage'=>'string'),
                                                        'out'   => array('outmessage'=>'string')
                                                     );
        }


The helloWorld method remains basically the same. The only difference is that we use SOAP_Fault for error
/* Of course, we also need to define all the functions that should
           be requestable through our server: */
        function helloWorld($inmessage)
        {
            /* Generate a SOAP error if the argument was not valid: */
            if($inmessage == '')
            {
                /* The SOAP error is generated by the SOAP_Fault class: */
                $fault = new SOAP_Fault('You must supply a valid string!','12345');
                return $fault->message();
            }
            else
            {
                /* If the argument is okay, we submit out return message: */
                return "Hello $inmessage!";
            }
        }
    }

Okay, we've done the definition of SOAP_Hello_Server, se it's time to start the service. First thing we need to do is to create an instance of SOAP_Server:
    /* Create a new SOAP server using PEAR::SOAP's SOAP_Server class: */
    $server = new SOAP_Server();
 
We also need to create an instance of our own class, SOAP_Hello_Server:
    /* Create an instance of our class: */
    $soaphelloserver = new SOAP_Hello_Server();

And then we tell SOAP_Server about our class:
    /* Register this instance to the server class: */
    $server->addObjectMap($soaphelloserver,array('namespace'=> 'urn:helloworld')););

Next, SOAP_Server->service() is called to start the service:
    /* The following line starts the actual service: */
    $server->service($HTTP_RAW_POST_DATA);

And, exit to avoid printing characters that could cause problems for our application:

Testing The Application

Hopefully, you have now managed to install PEAR::SOAP on your box, and you're ready to test the application. Point your browser to pearsoap-hello-client.php, and you should see "Hello World" on your browser.

Note that this article is a distillation from Wrox' forthcoming release - Professional PHP Webservices. The specific chapter (from which this article has been extracted) in the book has coverage on the other two leading SOAP implementations NuSOAP and eZSOAP. If you will read and toy around with them, you will notice that all three do exactly the same thing for us, save that each one is based on a different SOAP implementation. Ultimately it is your call as a developer, on which implementation to focus - Maybe the simplicity of NuSOAP appeals to you, or maybe it is the object oriented base of PEAR::SOAP that makes you feel more convenient? Nah! Don't decide just yet! Read the entire chapter from the book instead, and take a look at a couple of other examples that demonstrates the capabilities of PHP SOAP applications a bit further before you make that choice.

Hello World With PEAR::SOAP

After successfully installing PEAR::SOAP, the next step is to write a 'Hello World' using the implementation. To implement a SOAP server using PEAR::SOAP, you need to define a class that describes the server, and then pass an instance of this class over to PEAR::SOAP. PEAR::SOAPs SOAP_Server class will then use the characteristics of this class to set up a new SOAP service.

pearsoap-hello-client.php

pearsoap-hello-client.php is our PEAR::SOAP version of the Hello World consumer. Below youll find a listing and description of this script.
The SOAP_Client class is PEAR::SOAPs answer to NuSOAPs soapclient, and ezSOAPs eZSOAPClient classes. We need to include the definition of this class before we can proceed:
<?
    /* Include PEAR::SOAP's SOAP_Client class: */
    require_once('SOAP/Client.php');
SOAP_Clients requires one argument: the URL to the SOAP service we wish to connect to. Optionally, you can also pass an second boolean true to the script, which would indicate that the first argument is not an URL to a service, but a path to a WSDL:
    /* Create a new SOAP client using PEAR::SOAP's SOAP_Client-class: */
    $client = new SOAP_Client('http://localhost/soap/pear-soap/pearsoap-hello-server.php');

As usual, we send a single string to the server:
    /* Define the parameters we want to send to the server's helloWorld-function.
       Note that these arguments should be sent as an array: */
    $params = array('inmessage'=>'World');

Next, we make a call to the server, telling it we want to some assistance from its helloWorld method with the data in $params. When the request is carried out, the result gets stored in $response:
    /* Send a request to the server, and store its response in $response: */
$response = $client->call('helloWorld',$params,array('namespace'=> 'urn:helloworld'));

Print the result to the user:
    /* Print the server-response: */
    print $response;
?>