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)