A Factory Pattern is a creational design pattern which provides a uniform interface for creating objects related to each other.
Here is a simple example. Let us say that you want to connect to a database. There are so many ways of connecting: Oracle, MySQL, SQL Server,etc. We wish to provide a uniform interface for creating connections to these databases. The class which encapsulates this object creation is a “Factory”.
type CFactory private character(len=20) :: factory_type !! Descriptive name for database class(Connection), pointer :: connection_type !! Which type of database ? contains !! Note 'class' not 'type' ! procedure :: init !! Constructor procedure :: create_connection !! Connect to database procedure :: final !! Destructor end type CFactory
Since there are different types of database connections, let us create an abstract class to encapsulate all sorts of connections. For simplicity, each connection has only one procedure, description, which prints out what type of connection it itself is. Every derived type must implement this subroutine (hence, deferred).
type, abstract :: Connection contains procedure(generic_desc), deferred, pass(self) :: description end type Connection abstract interface subroutine generic_desc(self) import :: Connection class(Connection), intent(in) :: self end subroutine generic_desc end interface
We are now ready to create concrete connections derived from the above type. Let’s create two of them, Oracle and MySQL .
!! An Oracle connection type, extends(Connection) :: OracleConnection contains procedure, pass(self) :: description => oracle_desc end type OracleConnection !! A MySQL connection type, extends(Connection) :: MySQLConnection contains procedure, pass(self) :: description => mysql_desc end type MySQLConnection
The type-bound procedures are simple. They just print out who they are:
subroutine oracle_desc(self) class(OracleConnection), intent(in) :: self write(*,'(A)') "You are now connected with Oracle" end subroutine oracle_desc subroutine mysql_desc(self) class(MySQLConnection), intent(in) :: self write(*,'(A)') "You are now connected with MySQL" end subroutine mysql_desc
Now comes the crucial part. How do we implement our factory ? The constructor simply initializes the private variables, including pointers. We’ll be allocating memory to our polymorphic ‘class’ pointer, so the destructor has to free the memory.
subroutine init(self, string) class(CFactory), intent(inout) :: self character(len=*), intent(in) :: string self%factory_type = trim(string) self%connection_type => null() !! pointer is nullified end subroutine init subroutine final(self) class(CFactory), intent(inout) :: self deallocate(self%connection_type) !! Free the memory nullify(self%connection_type) end subroutine final
To create a connection, we simply search through (“if-else”) all the possible list of connections which our factory must be able to produce. This is the core part. A factory hides away how different objects are created.
function create_connection(self) result(ptr) class(CFactory) :: self class(Connection), pointer :: ptr if(self%factory_type == "Oracle") then if(associated(self%connection_type)) deallocate(self%connection_type) allocate(OracleConnection :: self%connection_type) ptr => self%connection_type elseif(self%factory_type == "MySQL") then if(associated(self%connection_type)) deallocate(self%connection_type) allocate(MySQLConnection :: self%connection_type) ptr => self%connection_type end if end function create_connection
So there you are. A pointer to the created object is returned. If the connection already exists and a new connection is being requested, we do so. We remove (_deallocate_) the old connection and freshly allocate a new connection.
The client program could be like this:
program main use factory_pattern implicit none type(CFactory) :: factory class(Connection), pointer :: db_connect => null() call factory%init("Oracle") db_connect => factory%create_connection() !! Create Oracle DB call db_connect%description() !! The same factory can be used to create different connections call factory%init("MySQL") !! Create MySQL DB !! 'connect' is a 'class' pointer. So can be used for either Oracle or MySQL db_connect => factory%create_connection() call db_connect%description() call factory%final() ! Destroy the object end program main
The same factory can be used to manufacture different objects as requested. Using polymorphism (“class pointers”) we can switch between different connections at runtime. The output of the above program is:
You are now connected with Oracle You are now connected with MySQL
PS: The above program compiles with GFortran 4.6.0 (experimental , built 19.June.2010)