Notes on PHP Design Patterns, Part 2: Patterns for Performing And Representing Tasks

In a previous notes entry I wrote about patterns used for Generating Objects, and facilitating Flexible Object Programming. In this entry I am writing about patterns used for Performing and Representing Tasks.

Performing and Representing Tasks

  • The Strategy Pattern:

Problem: When classes provide too much functionality, the code tends to pile up, making it difficult to maintain or use. A possible option for optimising this, is adding class inheritance, however this can lead to inheritance ballooning and code repetition.

Solution: Create a base class that allows client developers to select, at run time, which algorithms to use. For example, image filter processing can be separated into a base class 'FilterImage', which accepts an image resource and a filter type object (strategy object) as constructor parameters, thus delegating the filtering action to other classes of the similar types.

Example:

      class FilterImage {
            private $imageResource; // Stores the image resource
            private $filter; // Stores the filter class

            // Passes in the $imageResource image resource and the $filter object.
            public __construct( $imageResource, $filter ) {
                   $this->imageResource = $imageResource;
                   $this->filter = $filter;
            }

            // Method used for applying the configured filter
            public function apply() {
                   $this->filter->apply( $this->imageResource );
            }
      }

      interface Filter {
                // Called by the strategy class
                public function apply( $imageResource ) {}
      }

      class BlurFilter implements Filter {
            public function apply( $imageResource ) {
                   // Apply image blurring logic
            }
      }

      class SharpenFilter implements Filter {
            public function apply( $imageResource ) {
                   // Apply image sharpening logic
            }
      }

      // Example usage:
      $image = new FilterImage( $imageResource, new BlurFilter() );
      $image = new FilterImage( $image, new SharpenFilter() );
  • The Observer Pattern:

Problem: Certain functionality needs to be called when an event occurs, without hard coding it within the subject class.

Solution: This can be achieved by adding functions to 'attach()' or 'detach()' observer classes, and 'notify()' them once an event occurs within the observable class. For good design, the structure of Observers and Observable classes should be defined in interfaces.

Example:

      // Defines the structure of an observable class
      interface Observable {
                function attach( Observer $observer ) {}
                function notify() {}
                function detach( Observer $observer ) {}
      }

      // Defines the structure of an observer class
      interface Observer {
                // Called upon an even occurrence
                function update( Observable $observable ) {}
      }

      // Sample image upload class
      class ImageUpload implements Observable {
                // Stores an array of observers
                private $observers;

                // Attaches an observer
                function attach( Observer $observer ) {
                         $this->observers[] = $observer;
                }

                // Detaches an observer
                function detach( Observer $observer ) {
                         $this->observers = array_splice( 
                                            $this->observers
                                            ,array_search(
                                                          $observer
                                                          ,$this->observers
                                            )
                                            ,1
                         );
                }

                // Notifies all observers
                function notify() {
                         foreach ( $this->observers as $observer ) {
                                 $observer->update( $this );
                         }
                }

                // Notify all registered observers, upon successful image upload
                function imageUploaded() {
                         // [...] Other functionality.
                         $this->notify();
                }

                // [...] Other functionality.
      }
  • The Visitor Pattern:

Problem: In a previous entry, I described the Composite pattern used for handling tree-structure data. However, when working with collections of objects, client programmers might want to apply various operations that involve working with each individual component. Adding such operations on a case-by-case basis can lead to a bloated code base.

Solution: To avoid too much code within the structure's components, the composite and component classes should implement an 'accept()' function. This function constructs a method call, and invokes component specific 'visit()' methods within the visitor class, applied against all items within the tree.

Example:

      // Component class
      class Item {
            public function getName() {}
            // Main 'accept' method, that triggers a 'visit' 
            // call within the Visitor class
            public function accept( Visitor $visitor ) {
                   // Construct a method specific to this type of class
                   $method = "visit" . get_class( $this );
                   // Trigger a call within the visitor class
                   $visitor->$method( $this );
            }
      }

      // Leaf class
      class MenuItem extends Item {
            public function addMenuItem() {}
            public function getMenuItems() {}
            public function removeMenuItem() {}
      }

      // Composite class
      class Menu extends MenuItem {
            // Override the parent's accept class, to call
            // child items' accept method
            public function accept( Visitor $visitor ) {
                   // Call the 'accept' method for this component
                   parent::accept( Visitor $visitor );
                   // ...and it's subitems
                   foreach ( $this->getMenuItems() as $menuItem ) {
                           $menuItem->accept( $visitor );
                   }
            }
      }

      // Visitor class, implementing item type specific methods
      class Visitor {
            public function visitMenuItem( $node ) {}
            public function visitItem( $node ) {}
            public function visitMenu( $node ) {}
      }
  • The Command Pattern:

Problem: Some pages must handle many different types of tasks. 

Solution: We can encapsulate the tasks, thus making it easier to add new ones to our system.

Example:

      // Defines the 'command' interface
      interface Command {
                // Each command should make use of a context object
                public function execute( Context $context ) {}
      }

      // Sample commands
      class LoginCommand {
            public function execute( Context $context ) {
                   // Handle login
            }
      }

      class RegisterCommand {
            public function execute( Context $context ) {
                  // Handle registration
            }
      }

      // Creates a new instance of the right command
      class CommandFactory {
            public static function getCommand( $type ) {
                   switch ( strtolower( $type ) ) {
                          case "login":
                               return new LoginCommand();
                               break;
                          case "register":
                               return new RegisterCommand();
                               break;
                   }
            }
      }

      // Provides request context variables ( $_GET or $_POST )
      // NOTE: The $_REQUEST variable contains data from the $_COOKIE
      // variable. The get function should only return values stored
      // in $_GET or $_POST.
      class Context {
            public function get( $variableName ) {
                   return array_key_exists( $variableName, $_POST ) 
                          ? $_POST[$variableName] :
                          array_key_exists( $variableName, $_GET ) 
                          ? $_GET[$variableName] : null;
            }
      }

      // Delegates the command to command specific classes
      class Controller {
            private $context;
            public function __construct( $context ) {
                   $this->context = $context;
            }

            public function process() {
                $command = CommandFactory::get( $this->context->get( 'action' ) );
                $command->execute( $this->context );
            }
      }

NOTE: Due to it's complexity, the Interpreter Pattern will be explained in a future entry.

References: Some examples in this post rely on examples found in Matt Zandstra's second edition of 'PHP Objects, Patterns, and Practice'.

Leave a Reply

Your email address will not be published. Required fields are marked *