Abstract Factory

Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon
Reading Time: 1 minutes

Abstract Factory Design Pattern in Python

Abstract Factory Design Pattern in Python

Design Patterns Home
 

What is it?

Abstract Factory Design Pattern essentially builds on the Factory Design Pattern. It adds another level of encapsulation. It aims at providing the user with a way of creating objects of related classes (i.e. having a common characteristic, such as Knight, Rook etc.) at a given instance without exposing the exact class that is being instantiated, up until runtime. It is classified under Creational Patterns as it provides an industry standard procedure to create objects. It makes use of Polymorphism.

Let's look at the terms associated with this pattern.


Terminology

    Concrete Factory: A class which has a getter method, which returns an object of a class. The latter class, is one of many classes that denote different types of objects having a common characteristic (such as Knight, Rook, Bishop, they are chess pieces). For example, KnightFactory is a concrete factory which returns an object of type Knight.
  • Abstract Factory: A class which takes an object of one of the concrete classes as input, gets the object from it using the getter method of the concrete class, and provides a method to expose the details of the thus obtained object.

Pseudo Code

# Classes denoting different types of objects having a common characteristic.
class Knight:
	def directionOfMovement(): pass

class Rook:
	def directionOfMovement(): pass

# Concrete Classes: returns an object of the corresponding class
class KnightFactory:
	def getPiece(): returns a Knight object
	
class RookFactory:
	def getPiece(): returns a Rook object
	
# Abstract Factory: takes a concrete factory object as input, obtains the object from the factory, and provides a method to expose details of the object.
class PieceFactory:
	def __init__():
		assign the supplied concrete factory object to a local variable
	def detailsOfChosenPiece():
		call the getPiece() of the concrete factory object to obtain a piece
		access different methods and attributes of the piece such as directionOfMovement()
		
# USING THE CODE
create object of a concrete factory, say, RookFactory
create object of the abstract factory and supply to it the above object
using the above object of the abstract factory, call the utility method of the abstract factory i.e. detailsOfChosenPiece()

objectOfConcreteFactory = RookFactory()
objectOfAbstractFactory = PieceFactory(objectOfConcreteFactory)
objectOfAbstractFactory.detailsOfChosenPiece()

Why the need for it: Problem Statement

Last time around, we looked at the Simple Factory type of Factory Design Pattern. The Abstract Factory Pattern is another type of Factory Design Pattern, aiming at providing another level of encapsulation to the code. It addresses the same problems as the Simple Factory Pattern i.e. violation of Open/Closed Principle and the client having to be aware of all the concrete classes. The Abstract Factory Pattern is implemented when the user needs to be provided with a way of creating objects of related classes (i.e. having a common characteristic, such as Knight, Rook etc.) at a given instance without exposing the exact class that is being instantiated, up until runtime.


How to implement it

The Abstract Factory Pattern can be implemented in Python using:

  1. Different classes denoting different types of objects having a common characteristic.
  2. Concrete Factories having getter methods to return objects of above classes.
  3. Abstract Factory which takes a concrete factory object as input, obtains the object from the factory, and provides a method to expose details of the object.
# Different classes denoting different types of objects having a common characteristic: Knight, Rook & Bishop
class Knight:
    '''One of many classes having a common characteristic'''
    def directionOfMovement(self):
        return "Neither horizontally nor vertically."
    def stepsInMovement(self):
        return "2 and a half."
    def __str__(self):
        return "Knight"

class Rook:
    '''One of many classes having a common characteristic'''
    def directionOfMovement(self):
        return "Horizontally or vertically."
    def stepsInMovement(self):
        return "As many as 7."
    def __str__(self):
        return "Rook"

class Bishop:
    '''One of many classes having a common characteristic'''
    def directionOfMovement(self):
        return "Diagonally."
    def stepsInMovement(self):
        return "As many as 7."
    def __str__(self):
        return "Bishop"

# Concrete Factories having getter methods to return objects of above classes: KnightFactory, RookFactory & BishopFactory
class KnightFactory:
    '''Concrete Factory based on a class; returns an object of the corresponding class'''
    def getPiece(self):
        return Knight()

class RookFactory:
    '''Concrete Factory based on a class; returns an object of the corresponding class'''
    def getPiece(self):
        return Rook()

class BishopFactory:
    '''Concrete Factory based on a class; returns an object of the corresponding class'''
    def getPiece(self):
        return Bishop()

# Abstract Factory which takes a concrete factory object as input, obtains the object from the factory, and provides a method to expose details of the object: Piece Factory
class PieceFactory:
    '''An abstract factory which takes a concrete factory object as input, obtains the object from the factory, and provides a method to expose details of the object. '''
    def __init__(self, pieceFactory):
        self._pieceFactory = pieceFactory
        
    def detailsOfChosenPiece(self):
        '''utility method to display details of object returned by the abstract factory'''
        chosenPiece = self._pieceFactory.getPiece()
        print("Chosen piece:", chosenPiece)
        print("Direction of chosen piece:", chosenPiece.directionOfMovement())
        print("Number of steps the chosen piece can move:", chosenPiece.stepsInMovement())

objectOfConcreteFactory = RookFactory()
objectOfAbstractFactory = PieceFactory(objectOfConcreteFactory)
objectOfAbstractFactory.detailsOfChosenPiece()

### OUTPUT ###
Chosen piece: Rook
Direction of chosen piece: Horizontally or vertically.
Number of steps the chosen piece can move: As many as 7.

Walkthrough of implementation

  1. We obtain an object of one of the Concrete Factory (RookFactory) classes (objectOfConcreteFactory = RookFactory())
  2. Then, we supply the above object to the Abstract Factory (PieceFactory) and obtain its object(objectOfAbstractFactory = PieceFactory(objectOfConcreteFactory)). The __init__() method is called implicitly during object creation, which stores the object of the supplied concrete factory in a local variable.
  3. Using this object of the Abstract Factory, we obtain an object of the class Rook by making use of the utility method of the Abstract factory detailsOfChosenPiece(). This method first obtains the object of the class Rook using the getter method of the Concrete Factory RookFactory (chosenPiece = self._pieceFactory.getPiece()). Then, various methods of the object of class Rook are accessible.

Comparison of code when it is implemented and when it is not

BEFORE ABSTRACT FACTORY PATTERN:

class ChessPieceFactory:
    def createChessPiece(self, inputString):
        if inputString == "knight":
            return Knight()
        elif inputString == "rook":
            return Rook()
        elif inputString == "bishop":
            return Bishop() 
			
chessPieceFactory = ChessPieceFactory
pieceOne = chessPieceFactory.createChessPiece('knight')
pieceOne.someMethodOfKnightClass()

AFTER ABSTRACT FACTORY PATTERN:

# Classes denoting different types of objects having a common characteristic.
class Knight:
	def directionOfMovement(): pass

class Rook:
	def directionOfMovement(): pass

# Concrete Classes: returns an object of the corresponding class
class KnightFactory:
	def getPiece(): returns a Knight object
	
class RookFactory:
	def getPiece(): returns a Rook object
	
# Abstract Factory: takes a concrete factory object as input, obtains the object from the factory, and provides a method to expose details of the object.
class PieceFactory:
	def __init__():
		assign the supplied concrete factory object to a local variable
	def detailsOfChosenPiece():
		call the getPiece() of the concrete factory object to obtain a piece
		access different methods and attributes of the piece such as directionOfMovement()
		
# USING THE CODE
create object of a concrete factory, say, RookFactory
create object of the abstract factory and supply to it the above object
using the above object of the abstract factory, call the utility method of the abstract factory i.e. detailsOfChosenPiece()

objectOfConcreteFactory = RookFactory()
objectOfAbstractFactory = PieceFactory(objectOfConcreteFactory)
objectOfAbstractFactory.detailsOfChosenPiece()

Related to: Factory


 

 

 

 


See also:

Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon

Leave a Reply