Abstract Factory Pattern Assignment
Abstract Factory Pattern Assignment
Submitted by:
Nazish Naseer
CIIT/FA21-BSE-049/VEHARI
Subject:
Software Architecture
Submitted to:
Madam Komal Hassan
Abstract Factory Pattern
The Abstract Factory Pattern is a creational design pattern that provides an
interface for creating families of related or dependent objects without specifying their
concrete classes. It allows you to produce a set of related products (like different UI
components, vehicles, or shapes) while ensuring that the client code can work with these
products without needing to know the specific implementations.
The pattern works by defining an abstract factory interface that declares methods for creating
abstract products (like a button, checkbox, car, or bike). Then, concrete factories implement
this interface to create specific versions of the products. This is useful for creating objects
that are grouped together, such as all products for a specific operating system or brand.
For example, an abstract factory can create both Windows and Mac UI components,
ensuring that the application uses the correct set of components for a given platform.
Examples:
class IBike:
def manufacture(self):
pass
class MahindraQuanto(ICar):
def manufacture(self):
return "Mahindra Quanto is manufactured"
# Concrete Bikes
class Pulsar(IBike):
def manufacture(self):
return "Pulsar Bike is manufactured"
class MahindraScooty(IBike):
def manufacture(self):
return "Mahindra Scooty is manufactured"
# Mahindra Factory
class MahindraMotors(IAutomobileManufacture):
def getCar(self) -> ICar:
return MahindraQuanto()
# Client Code
def client(factory: IAutomobileManufacture):
car = factory.getCar()
bike = factory.getBike()
print(car.manufacture())
print(bike.manufacture())
# Example usage
bajaj_factory = BajajMotors()
mahindra_factory = MahindraMotors()
print("Bajaj Factory:")
client(bajaj_factory)
print("\nMahindra Factory:")
client(mahindra_factory)
These are abstract classes (or interfaces) for the products that factories produce: cars
and bikes.
They declare the method manufacture() that will be implemented by the concrete
classes.
Concrete Products:
Client Code:
The client uses the factory to create cars and bikes without knowing the specific types
(concrete classes).
The client() function interacts with the abstract factory interface, making it flexible
to work with different brands by simply switching the factory.
Output:
Bajaj Factory:
Bajaj Car is manufactured
Pulsar Bike is manufactured
Mahindra Factory:
Mahindra Quanto is manufactured
Mahindra Scooty is manufactured
operating systems (Windows and Mac)
Sample Code
class Checkbox(ABC):
@abstractmethod
def paint(self):
pass
class MacCheckbox(Checkbox):
def paint(self):
return "Rendering a Mac Checkbox."
@abstractmethod
def createCheckbox(self) -> Checkbox:
pass
def createUI(self):
self.button = self.factory.createButton()
self.checkbox = self.factory.createCheckbox()
def paint(self):
print(self.button.paint())
print(self.checkbox.paint())
# Example Usage
def configure_application(os_type: str):
if os_type == "Windows":
factory = WinFactory()
elif os_type == "Mac":
factory = MacFactory()
else:
raise ValueError("Unknown operating system.")
app = Application(factory)
app.createUI()
app.paint()
print("\nMac Environment:")
configure_application("Mac")
Explanation of code:
These are interfaces (abstract classes in Python) for the products that the factories will
produce.
Each of these defines the paint() method that concrete products must implement.
These are the concrete implementations of the Button and Checkbox interfaces.
For each operating system (Windows and Mac), there are corresponding classes for
buttons and checkboxes that implement the paint() method.
Abstract Factory (GUIFactory):
This is the interface for creating related products (buttons and checkboxes).
It declares methods createButton() and createCheckbox() which return instances
of Button and Checkbox.
These concrete factories implement the GUIFactory interface and create specific
button and checkbox objects depending on the operating system.
Client (Application):
The Application class uses the factory to get instances of buttons and checkboxes.
The method createUI() creates the button and checkbox, and the paint() method
renders them.
Output:
Windows Environment:
Rendering a Windows Button.
Rendering a Windows Checkbox.
Mac Environment:
Rendering a Mac Button.
Rendering a Mac Checkbox.
Sample Code:
from abc import ABC, abstractmethod
class UndeadEnemy(Enemy):
def attack(self):
return "The undead enemy attacks with dark magic!"
class AlienEnemy(Enemy):
def attack(self):
return "The alien enemy attacks with plasma beams!"
class RobotEnemy(Enemy):
def attack(self):
return "The robot enemy attacks with lasers!"
# Abstract Factory
class AbstractEnemyFactory(ABC):
@abstractmethod
def createEnemy(self, type: str) -> Enemy:
pass
# Concrete Factories
class UndeadFactory(AbstractEnemyFactory):
def createEnemy(self, type: str) -> Enemy:
if type == "Zombie":
return Zombie()
elif type == "Vampire":
return Vampire()
else:
raise ValueError(f"Unknown undead enemy type: {type}")
class AlienFactory(AbstractEnemyFactory):
def createEnemy(self, type: str) -> Enemy:
if type == "Martian":
return Martian()
elif type == "Predator":
return Predator()
else:
raise ValueError(f"Unknown alien enemy type: {type}")
class RobotFactory(AbstractEnemyFactory):
def createEnemy(self, type: str) -> Enemy:
if type == "Android":
return Android()
elif type == "Cyborg":
return Cyborg()
else:
raise ValueError(f"Unknown robot enemy type: {type}")
# Concrete Enemy Types for Undead
class Zombie(UndeadEnemy):
def attack(self):
return "Zombie lumbers towards you!"
class Vampire(UndeadEnemy):
def attack(self):
return "Vampire swoops in for a bite!"
class Predator(AlienEnemy):
def attack(self):
return "Predator hunts you down silently!"
class Cyborg(RobotEnemy):
def attack(self):
return "Cyborg strikes with enhanced strength!"
# Client Code
def client_code(factory: AbstractEnemyFactory, enemy_type: str):
enemy = factory.createEnemy(enemy_type)
print(enemy.attack())
# Example Usage
if __name__ == "__main__":
print("Creating Undead Enemies:")
undead_factory = UndeadFactory()
client_code(undead_factory, "Zombie")
client_code(undead_factory, "Vampire")
This defines the common interface for all enemies, including the attack() method
that each concrete enemy must implement.
Each concrete enemy class implements the attack() method to define how the
enemy attacks.
This defines the method createEnemy() which is responsible for creating different
types of enemies.
Client Code:
The client code interacts with the abstract factory to create specific types of enemies.
It doesn't need to know the exact class of the enemy, only that it is creating one.
Output:
Creating Undead Enemies:
Zombie lumbers towards you!
Vampire swoops in for a bite!
Conclusion:
The Abstract Factory Pattern provides a powerful way to create families of related
or dependent objects without specifying their concrete classes. This pattern ensures that the
client code remains decoupled from the creation process, making it easier to introduce new
enemy types or families without modifying existing code.