# Factory Pattern (Python)

What is Factory Pattern?

The factory design pattern is a creational pattern that provides a way to create objects without specifying the exact class of the object that will be created. This is useful when the process of creating an object is complex or you want to abstract the object creation process from the rest of the application.

**Example:**

```python
class Dog:
    def __init__(self, name):
        self._name = name

    def speak(self):
        return "Woof!"

class Cat:
    def __init__(self, name):
        self._name = name

    def speak(self):
        return "Meow!"

def get_pet(pet="dog"):
    pets = dict(dog=Dog("Hope"), cat=Cat("Peace"))
    return pets[pet]

d = get_pet("dog")
print(d.speak())

c = get_pet("cat")
print(c.speak())
```

In this example, we have two classes `Dog` and `Cat` each with their own `speak` method. The `get_pet` function acts as a factory that takes a string `pet` as a parameter and returns an instance of either the `Dog` or `Cat` class, depending on the value of the `pet` parameter. The factory function uses a dictionary `pets` to map the string values to the respective classes.  
  
By using the factory pattern, the rest of the application does not need to know about the specific classes that are being created, it only needs to know about the `get_pet` function and the `speak` method, which provides a clean and abstract interface for creating objects.  
  
**More Realistically**:

```python
from abc import ABC, abstractmethod

class DatabaseConnection(ABC):
    @abstractmethod
    def connect(self):
        pass

class MySQLConnection(DatabaseConnection):
    def connect(self):
        return "MySQL connection established."

class PostgreSQLConnection(DatabaseConnection):
    def connect(self):
        return "PostgreSQL connection established."

class DatabaseConnectionFactory:
    @staticmethod
    def create_connection(db_type: str) -> DatabaseConnection:
        if db_type == "mysql":
            return MySQLConnection()
        elif db_type == "postgresql":
            return PostgreSQLConnection()
        else:
            raise ValueError(f"Unknown database type: {db_type}")

# Usage
factory = DatabaseConnectionFactory()
connection = factory.create_connection("mysql")
print(connection.connect())  # Output: MySQL connection established.
```

In short, using the Factory Pattern in the example provides the following key benefits:

1. **Encapsulation**: Centralizes and simplifies object creation logic, making it easier to manage and change without affecting client code.
    
2. **Loose Coupling**: Decouples client code from specific classes, allowing it to work with abstract interfaces and promoting flexibility.
    
3. **Single Responsibility**: Keeps object creation responsibility within the factory, while connection classes focus solely on their behavior.
    
4. **Extensibility**: Easily add new types of database connections without modifying existing client code.
    
5. **Runtime Flexibility**: Allows dynamic selection of connection types at runtime based on input or configuration.
    
6. **Improved Maintenance**: Standardizes object creation, making the codebase more readable and easier to maintain.
    
7. **Testing Ease**: Facilitates testing by allowing controlled creation of objects, aiding in mocking and stubbing.  
      
    DONE :D
