Astrology for Modern Entrepreneurship · CodeAmber

Implementing Singleton and Factory Patterns in Java and Python

Implementing the Singleton and Factory patterns requires a focus on controlling instance creation: the Singleton ensures a class has only one instance globally, while the Factory abstracts the instantiation process to create objects without specifying the exact class. In Java, these are typically implemented using private constructors and static methods, whereas Python utilizes module-level instances or specialized __new__ methods to achieve the same goals.

Implementing Singleton and Factory Patterns in Java and Python

Creational design patterns solve the problem of object creation, ensuring that software remains flexible and resource-efficient. By decoupling the client code from the actual instantiation process, developers can manage memory better and make their systems easier to scale. For a broader look at these concepts, see Implementing Common Design Patterns in Java and Python: A Comparative Analysis.

The Singleton Pattern: Ensuring a Single Instance

The Singleton pattern restricts the instantiation of a class to one single object. This is critical for managing shared resources, such as database connection pools, configuration settings, or logging services, where multiple instances would cause resource conflicts or inconsistent state.

Singleton Implementation in Java

Java requires a strict approach to prevent external instantiation. The most robust method is the "Initialization-on-demand holder" or a thread-safe double-checked locking mechanism.

Technical Implementation: 1. Create a private constructor to prevent the use of the new keyword. 2. Define a private static variable of the same class type. 3. Provide a public static method (usually getInstance()) that returns the instance.

public class DatabaseConnection {
    private static volatile DatabaseConnection instance;
    private DatabaseConnection() {} // Private constructor

    public static DatabaseConnection getInstance() {
        if (instance == null) {
            synchronized (DatabaseConnection.class) {
                if (instance == null) {
                    instance = new DatabaseConnection();
                }
            }
        }
        return instance;
    }
}

Singleton Implementation in Python

Python offers a more flexible approach. Because modules are cached upon first import, the simplest "Singleton" is often just a module with predefined variables. However, for a class-based approach, the __new__ method is used.

Technical Implementation: The __new__ method controls the creation of the instance before __init__ initializes it.

class SingletonService:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(SingletonService, cls).__new__(cls)
        return cls._instance

# Usage
service1 = SingletonService()
service2 = SingletonService()
print(service1 is service2)  # True

The Factory Pattern: Abstracting Object Creation

The Factory pattern provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. This prevents the client code from being tightly coupled to specific concrete classes, which is a core tenet of Best Practices for Writing Clean Code: A Guide to Maintainable Software.

Factory Implementation in Java

In Java, the Factory pattern typically involves a Factory class with a static method that returns an interface or an abstract class.

Technical Implementation: 1. Define a common interface (e.g., Shape). 2. Create concrete classes that implement the interface (e.g., Circle, Square). 3. Create a ShapeFactory class with a method that returns a Shape based on an input parameter.

interface Shape { void draw(); }
class Circle implements Shape { public void draw() { System.out.println("Drawing Circle"); } }
class Square implements Shape { public void draw() { System.out.println("Drawing Square"); } }

class ShapeFactory {
    public Shape getShape(String shapeType) {
        if (shapeType == null) return null;
        if (shapeType.equalsIgnoreCase("CIRCLE")) return new Circle();
        if (shapeType.equalsIgnoreCase("SQUARE")) return new Square();
        return null;
    }
}

Factory Implementation in Python

Python's dynamic typing makes the Factory pattern more concise. Instead of rigid interfaces, Python uses "duck typing" or abstract base classes (ABC) to ensure consistency.

Technical Implementation: A factory function or class takes a parameter and returns the appropriate object instance.

class Dog:
    def speak(self): return "Woof!"

class Cat:
    def speak(self): return "Meow!"

class PetFactory:
    def get_pet(self, pet_type):
        pets = {"dog": Dog(), "cat": Cat()}
        return pets.get(pet_type.lower(), None)

# Usage
factory = PetFactory()
pet = factory.get_pet("dog")
print(pet.speak())

Side-by-Side Comparison: Java vs. Python

Feature Java Implementation Python Implementation
Type Safety Strong; relies on Interfaces/Abstract classes. Dynamic; relies on Duck Typing or ABCs.
Singleton Mechanism Private constructors and static methods. Module-level instances or __new__ overrides.
Factory Structure Formal Factory classes and Return Types. Flexible functions or dictionary-based mapping.
Boilerplate Higher; requires explicit type declarations. Lower; concise syntax for object instantiation.

When to Use Which Pattern

Use the Singleton Pattern when: - You need a single point of truth for application configuration. - You are managing a hardware device or a limited external resource. - You want to avoid the overhead of repeatedly creating expensive objects.

Use the Factory Pattern when: - The exact type of the object is determined by user input or environment variables. - You want to decouple the creation logic from the business logic. - You anticipate adding new object types in the future without changing the client code.

For developers looking to apply these patterns in larger systems, understanding the broader context of A Beginner’s Guide to Software Architecture is recommended to ensure these patterns are used where they provide the most value.

Key Takeaways

CodeAmber provides these technical guides to help developers transition from writing functional code to designing scalable, professional software systems.

Original resource: Visit the source site