The concrete factory
A concrete factory implements the abstract factory "create" methods. It will be the only point in the code that we need to update after add a new checkout system. This is a very specialized class which only responsibility is to create the proper parts. Our application has two concrete factories: FujitsuConcreteFactory and HPConcrete Factory. If you analyse the code carefully you maybe realize that we could implement only one factory
Código COBOL:
CLASS-ID. FujitsuConcreteFactory as "AFP.FujitsuConcreteFactory".
environment division.
configuration section.
repository.
interface ICheckoutAbstractFactory as "AFP.ICheckoutAbstractFactory"
interface ICashDrawerAbstractProduct as "AFP.ICashDrawerAbstractProduct"
interface IReceiptPrinterAbstractProduct as "AFP.IReceiptPrinterAbstractProduct"
*>Cash drawers classes
class ClassTeamPos1054258002 as "AFP.Drivers.CashDrawers.TeamPoS105428002"
class ClassTeamPos10PB60014 as "AFP.Drivers.CashDrawers.TeamPoS0PB60014"
*>Receipt printers classes
class ClassTeamPOSFD21 as "AFP.Drivers.ReceiptPrinters.TeamPOSFD21"
class ClassTeamPOSFD22 as "AFP.Drivers.ReceiptPrinters.TeamPOSFD22"
class ClassUtils as "AFP.Utils"
class CollectionHashTable as "System.Collections.Hashtable"
class ?SystemString as "System.String"
class ?SystemObject as "System.Object".
object. implements ICheckoutAbstractFactory.
data division.
working-storage section.
01 checkoutHardware usage object reference SystemString.
01 receiptPrinterName pic n(80) value spaces.
01 cashDrawerName pic n(80) value spaces.
01 driver usage object reference SystemObject.
01 drivers ?usage object reference CollectionHashTable.
procedure division.
method-id. NEW.
procedure division.
*> Getting all drivers at once
invoke ClassUtils "GetCheckoutDrivers" returning drivers.
end method NEW.
method-id. CreateCashDrawer as "CreateCashDrawer".
data division.
linkage section.
01 cashDrawer usage object reference ICashDrawerAbstractProduct.
procedure division returning cashDrawer.
*> Get cashDrawer and receipt drivers
set driver to drivers::"get_Item"("CashDrawer")
set cashDrawerName to driver as SystemString
*> creating the cash drawer class defined in the configuration file
evaluate cashDrawerName
when n"TeamPos1054258002"
invoke ClassTeamPos1054258002 "NEW" returning cashDrawer
when n"TeamPos10PB60014"
invoke ClassTeamPos10PB60014 "NEW" returning cashDrawer
end-evaluate
end method CreateCashDrawer.
method-id. CreateReceiptPrinter as "CreateReceiptPrinter".
data division.
linkage section.
01 receiptPrinter usage object reference IReceiptPrinterAbstractProduct.
procedure division returning receiptPrinter.
*> Get cashDrawer and receipt drivers
set driver to drivers::"get_Item"("ReceiptPrinter")
set receiptPrinterName to driver as SystemString
*> creating the receipt printer class defined in the configuration file
evaluate receiptPrinterName
when n"TeamPoSFD21"
invoke ClassTeamPoSFD21 "NEW" returning receiptPrinter
when n"TeamPoSFD22"
invoke ClassTeamPoSFD22 "NEW" returning receiptPrinter
end-evaluate
end method CreateReceiptPrinter.
end object.
END CLASS FujitsuConcreteFactory.
The abstract product
An abstract product is the interface that allow the concrete factory to load the proper product. Without that interface, the concrete factory would be useless (or much more complex than the necessary). Below the interface for Receipt Printers:
Código COBOL:
INTERFACE-ID. IReceiptPrinterAbstractProduct as "AFP.IReceiptPrinterAbstractProduct".
environment division.
configuration section.
repository.
interface ICashDrawerAbstractProduct as "AFP.ICashDrawerAbstractProduct"
class SystemString as "System.String"
class ClassUser as "AFP.User"
class ClassCompany as "AFP.Company"
class ClassProduct as "AFP.Product".
procedure division.
method-id. StartNewSale as "StartNewSale".
data division.
linkage section.
01 company ?usage object reference ClassCompany.
01 user usage object reference ClassUser.
procedure division using by value company, by value user.
end method StartNewSale.
method-id. RegisterItem as "RegisterItem".
data division.
linkage section.
01 product?usage object reference ClassProduct.
01 quantity usage binary-long signed.
procedure division using by value product, by value quantity.
end method RegisterItem.
method-id. PrintTotal as "PrintTotal".
data division.
linkage section.
01 cashDrawer usage object reference ICashDrawerAbstractProduct.
procedure division using by value cashDrawer.
end method PrintTotal.
*> ... and any other interfaces that you may need to implement
*> (e.g. CancelItem, CancelTransaction, PrintDiscount etc
END INTERFACE IReceiptPrinterAbstractProduct.
The concrete product
A concrete product in our checkout architecture take care of all communications with the hardware, so we are calling them drivers in this context. Here is the a concrete product for a Fujitsu Cash Drawer:
Código COBOL:
CLASS-ID. TeamPoS0PB60014 as "AFP.Drivers.CashDrawers.TeamPoS0PB60014".
environment division.
configuration section.
repository.
interface ICashDrawerAbstractProduct as "AFP.ICashDrawerAbstractProduct".
object. implements ICashDrawerAbstractProduct.
procedure division.
method-id. OpenDrawer as "OpenDrawer".
procedure division.
display "Fujitsu cash drawer TeamPoS0PB60014 opened..."
*> here would go specific cash drawer API calls...
end method OpenDrawer.
end object.
END CLASS TeamPoS0PB60014.
Using our factory
So, here is our abstract factory in action. You will notice that we are no longer directly instantiating the driver´s classes. Actually, that code has no idea which driver has been choosen. This has been delegated to the concrete factory. One very insteresting aspect is that our checkout parts can talk each other (for instance, when closing a sale, the receipt printer would open the cash drawer automatically).
Código COBOL:
CLASS-ID. ProcessOrder AS "NowYouAreTalking.ProcessOrder".
*> ...
method-id. ProcessNewOrder as "ProcessNewOrder".
*> ...
procedure division.
*> ...
*> Creating a concrete factory
invoke ClassFujitsuConcreteFactory "NEW" returning checkout
*> Creating cash drawer and receipt printer classes
invoke checkout "CreateCashDrawer"returning cashDrawer
invoke checkout "CreateReceiptPrinter" returning receiptPrinter
*> ...
*> Using receipt printer
invoke receiptPrinter "StartNewSale" using aCompany, aUser
invoke receiptPrinter "RegisterItem" using aProduct, 10
*> close order and open the cashDrawer
invoke receiptPrinter "PrintTotal" using cashDrawer
end method ProcessNewOrder.
end object.
END CLASS ProcessOrder.
Abstract Factories are eveywhere
ATMs, RTS games, DB connections, web frameworks and many more applications uses that useful pattern. Some examples:
Spring.Net (Web Framework)
.Net ADO.Net
Microsoft COM (yes, even the COM implements that pattern)
Download the source
Cool things with OO Cobol - Source Code
Some references in the web
Abstract Factory .NET Design Pattern in C# and VB - dofactory.com
Abstract factory pattern - Wikipedia