Factory Interface Example

Factory Interface Example

Database access for Web-based applications is not uncommon, so assuming that you require access to a database, how do you decide which query method to use if you are required to support multiple databases? Sometimes, you can't make that decision ahead of time; it's dependent on what the client has or wishes to use. This is frequently the case if you are creating Web-based applications for a large audience, such as an open-source project.

In this case, assume that MySQL, PostgreSQL and Interbase are all feasible options. These are some simplified code snippets that might be used to create each connection.

  • MySQL:

       $query = mysql_query ($sql, $database_handle); 
  • Postgres:

       $query = pg_query ($database_handle, $sql); 
  • Interbase:

       $query = ibase_connect ($database_handle, $sql); 
  • The Old School Approach

    Assuming that you have populated a variable ($databasetype) with the required database type, you could use a switch() statement, as below, to figure out what method to use:

       // run query    switch (strtolower($databasetype)) {        case "mysql":            $query = mysql_query ($sql, $database_handle);            break;        case "postgres":            $query = pg_query ($database_handle, $sql);            break;        case "interbase":            $query = ibase_query ($database_handle, $sql);            break;        default:            die ("Unknown database type:".$databasetype);    } 

    This code will work, no question about it. But what if somebody later needs you to add support for SQL Server? You would have to go through your code and add these lines wherever you are executing your queries:

       case "mssql":        $query = mssql_query ($sql, $database_handle); 

    Imagine then you start getting complaints that your software doesn't work with Interbase. How can that be? Interbase is supported. Looking further into the problem, people are entering firebird when they mean interbase. Although it's an honest mistake, the Interbase product (by Borland) was also released as open source software called Firebird. Regardless, you have to go back to your code and add the following:

       case "interbase":    case "firebird":        $query = ibase_query ($database_handle, $sql); 

    Although making those seemingly trivial changes isn't hard, your switch structure would most likely be replicated throughout your code and not just for executing queries, either. Closing the database might be accomplished in the following way:

       // close database    switch (strtolower($databasetype)) {        case "mysql":            mysql_close ($database_handle);            break;        case "postgres":            pg_close ($database_handle);            break;        case "interbase":        case "firebird":            ibase_close ($database_handle);            break;        case "mssql":            mysql_close ($database_handle);        default:            die ("Unknown database type:".$databasetype);    } 

    Have you noticed that expanding the functionality even by a little can lead to a whole lot more work in effect, tracking down every place the changes should be replicated? It gets even more complicated if you throw in the fact that you may not want to close connections, such as when using persistent connections.

    The code we have just looked at, although syntactically and technically correct, lead to a wide distribution of required modifications borne out of subsequent support for new database platforms. For instance, if support for SuperCoolDatabase were introduced, changes would have to be made in many different places in the code base. Since developers are human, any dependency upon multiple source- code modifications tends to lead to errors and omissions, which is obviously something best avoided. This situation, or smell (as in "something smells not quite right here") can be avoided altogether by using what is known as the Factory Method design pattern.

    The Factory Interface Approach

    Clearly, in our recurring example, it is impossible for you as a developer to decide ahead of time which databases you'll need to support. How can you plan for future expansion, therefore, whilst ensuring your code remains maintainable and readable?

    You might consider using the factory method design pattern as one possible solution the problem. It works by abstracting the creation of objects (that is, instantiation) by using inheritance. In the factory method design pattern, an interface is defined that represents a standard by which subclasses can specify the correct class to instantiate.

    Figure 9-1 shows an overview of the factory method in UML(Gamma et al., Design Patterns).

    Figure 9-1

    The key component here is the Creator class, which defines the factory interface method. Here it is listed as FactoryMethod.

    The running instance of the ConcreteCreator class dutifully offers its unique implementation of the FactoryMethod. The FactoryMethod relies on the ConcreteCreator (of which there may be many different flavors) to instantiate the correct Product class, listed here as ConcreteProduct. You are, of course able to have other lifecycle or utility operations in the ConcreteCreator class, demonstrated here as AnOperation.

    Using the Factory Interface in Database Abstraction

    First, consider the instance of the class that you are required to create at runtime. That class should always be the center of your programming attention. In the following example, the code requires you to throw a special exception depending on which database you're running. Since you can't determine exactly which database your client is using at their end, the choice of which type of exception to throw will be left up to the Factory Interface.

    Here follows an abbreviated example utilizing a PostgreSQL Database (Figure 9-2).

    Figure 9-2

       interface ExceptionFactory {       public function getException($result=NULL, $message=NULL);    }    class PostgresExceptionFactory implements ExceptionFactory {        public function getException($result=NULL, $message=NULL) {            return new PostgresException ($result, $message);        }    }    class PostgresException extends Exception {        function __construct($result=NULL, $message=NULL) {            $code = 0;            if ($message == NULL) {                if ($result <> NULL) {                    $mess ge = pg_result_error ($result);                    $code = pg_result_status($result);                } else {                    $message = pg_last_error();                }            }            parent::__construct ($message, $code);        }    }    class MySqlExceptionFactory implements ExceptionFactory {        public function getException($result=NULL, $message=NULL) {            return new MySqlException ($result, $message);        }    }    class MySqlException extends Exception {        function __construct($result=NULL, $message=NULL) {            if ($message == NULL) {                $message = mysql_error();            }            $code = 0;            if ($result <> NULL) {                $code = mysql_errno($resource);            }            parent::__construct ($message, $code);        }    }    /*     * pseudo-code example usage     */    // create your required factory    $factory = new PostgresExceptionFactory();    // getException returns a PostgresException    if ( ... something goes horribly wrong ... ) {        throw $factory->getException();    } 

    Let's work our way through the example diagram and code above:

    1. After your client determines which database will be used to support your application, it can be adapted to make use of the relevant ExceptionFactory implementation. If none exists, one can be easily written by following the edicts of the ExceptionFactory interface.

    2. When your application must throw an exception, it does so by calling the getException method of the appropriate ExceptionFactory class.

    3. The getException method represents the factory interface in this case. Because it is implemented by a specific instantiated class, it returns the correct type of exception for the database being used.

    This example is simple by design. Notice how the ExceptionFactory interface relies on its subclasses PostgresExceptionFactory and MysqlExceptionFactory. They should be the only classes to instantiate your exceptions.

    Professional PHP5 (Programmer to Programmer Series)
    Professional PHP5 (Programmer to Programmer Series)
    ISBN: N/A
    EAN: N/A
    Year: 2003
    Pages: 182

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net