SitePoint Sponsor

User Tag List

Results 1 to 4 of 4
  1. #1
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)

    Of Interfaces - Tables and Fields.

    The current codebase of my employer has a nightmarish use of interfaces. Of the some 200 classes in the system, each and every one has a matching interface. Not one of those interfaces is reused.

    I haven't coded interfaces much. I've begun to make heavy use of the ones in PHP, but this isn't exactly the same thing in my opinion because native PHP interfaces often modify the behavior of the underlying scripting engine. ArrayAccess and Iterator are two powerful and extremely useful examples.

    Recently I've begun work on an interface when it became clear that I needed two rather disparate classes to do certain things exactly the same way. Specifically the table and the field class. The reason is that a table can be a field.

    I want the code to handle this most common case of relationships between two tables - parent to child; one to many - pretty transparently.

    Now unlike Doctrine or Hibernate I don't want to build a new class every time I add a table to the database, or run a script to update that class when the field map changes.

    I'm still working on both classes and the interface is being written for two reasons - to enforce that their shared concerns are handled the same way as far as the outside world is concerned. Also I want to use instanceof to detect the interface on occassion.

    I'm further along with the base field class than the table at the moment. The field class is a caster and validator. It knows the minimum, maximum, and default values of the field, and whether the field value is allowed to be null, is required, is unique within the table, or unique among siblings. Extending classes will add other validations. All validations are written as closures and stored in a checks array. The class is to build this array of closures at construct time. Many of these closures are almost embarrassingly simple stuff like this snippet which defines the null check.

    Code php:
    if (!$this->allowNull) {
      $this->checks['null'] = function($val) {
        return !is_null($val)
      };
    }

    The row class actually does the validation. It extends ArrayObject and performs validation when offsetSet is called.

    Code php:
    public function offsetSet($offset, $value) {
      if ($this->valid($offset, $value)) {
        parent::offsetSet($offset, $value);
      } else {
        throw new ValidationException();
      }
    }
     
    protected function valid ($offset, $value ) {
      $this->errors = array();
     
      foreach ($this->fields[$offset]->checks as $test => $valid) {
        if ($valid($value)) {
          continue;
        } else {
          $this->errors[$offset][$test] = $value;
        }
      }
     
      return count($this->errors) == 0;
    }

    Note the above is simplified a *little* but not much. The purpose to the closures approach here is that the row class that is doing the validation doesn't need to know the details of validation for its fields.

    And the thought here is that it also doesn't need to know that the "field" is a table either

    This is still heavily in flux, but I'm posting out some of this here to get some commentary since I haven't done much in the way of using interfaces.

  2. #2
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    989
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    I'm still working on both classes and the interface is being written for two reasons - to enforce that their shared concerns are handled the same way as far as the outside world is concerned. Also I want to use instanceof to detect the interface on occassion.
    This is exactly when to use interfaces. When you have two classes doing the same thing to a different back end and you want to enable polymorphism even though the classes aren't the same.

    An interface for every class is as pointless as it is redundant.

    Interfaces are also useful when you want to delegate beavior to another object but you don't know (or even care) how the internals will work. You might provide a class and an interface, then the client code can supply the missing behaviour as it knows what it has to do.

    One thing that is missing in interfaces, imho, is the ability to hint the return type.

  3. #3
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    One thing that is missing in interfaces, imho, is the ability to hint the return type.
    Oh hells yes. Not merely hint but enforce. I have code that is going to barf if the return isn't a specific type (array). Then again, return type hinting is on the table for PHP 5.4 I believe.

    In the meanwhile I'll rely on the comment text to hint of what it should be.

  4. #4
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    I'm getting the hang of this idea - I've created my second interface and again, it's because what is going on to meet the requirements of the interface are very different. In this event I needed FileLibraries to consistently do certain tasks even though some are children of ReadOnlyArray and some are children of the native ArrayObject. Here it is with it's three functions...

    Code php:
    namespace Gazelle;
    /**
     * File Libraries are collections of files with a shared purpose.
     * Templates and classes are the two most common.
     * 
     * @author Michael
     * @package Gazelle Core
     */
    interface FileLibrary {
    	/**
    	 * Get a copy of the array that holds the library.  Used by the
    	 * factories to create libraries from libraries, and by the test
    	 * system to check the integrity of the created library.
    	 * 
    	 * @return array
    	 */
    	public function getArrayCopy();
     
    	/**
    	 * Checks to see if a library object exists based on its label.
    	 * @param string $label
    	 * @return boolean
    	 */
    	public function exists( $label );
     
    	/**
    	 * Find a library object based on it's label. Different File Libraries
    	 * are free to parse this label in different ways - how template
    	 * libraries go about this is very different from the loader for
    	 * example. The point here is that both classes have a find method.
    	 */
    	public function find( $label );
    }

    For the curious, The autoload method of the framework implements this interface and the find method is the load method of that class.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •