All Courses
OOP: DESIGN PATTERNS

Factory Pattern

Concept Definition

Factory centralizes object creation behind a function or class. Instead of letting every caller know which concrete class to construct, the caller asks for an object by role. The returned object is usually handled through a base-class pointer such as std::unique_ptr<Notifier>.

Think of a restaurant counter. You order "tea" or "coffee"; you do not walk into the kitchen and build the drink yourself.

Why It Matters

CSE students often write long if/else blocks in main() to decide which derived class to create. That works for tiny programs, but it spreads construction rules across the codebase. A Factory keeps creation logic in one place, which makes new derived classes easier to add and test.

Factory also pairs naturally with abstraction. The caller depends on the base interface, while the factory knows the concrete implementation.

Syntax Block

std::unique_ptr<Base> makeObject(const std::string& kind) {
    if (kind == "name") {                    // choose concrete type
        return std::make_unique<Derived>();  // return as base pointer
    }
    throw std::invalid_argument("bad kind"); // reject unknown requests
}

Explained Code

Example: Notification Factory

#include <memory>      // std::unique_ptr, std::make_unique
#include <stdexcept>   // std::invalid_argument
#include <string>      // std::string

class Notifier {
public:
    virtual ~Notifier() = default;                 // allow safe polymorphic deletion
    virtual std::string channel() const = 0;       // common interface
};

class EmailNotifier : public Notifier {
public:
    std::string channel() const override { return "email"; } // concrete behavior
};

class SmsNotifier : public Notifier {
public:
    std::string channel() const override { return "sms"; }   // concrete behavior
};

std::unique_ptr<Notifier> makeNotifier(const std::string& type) {
    if (type == "email") { return std::make_unique<EmailNotifier>(); } // build email
    if (type == "sms") { return std::make_unique<SmsNotifier>(); }     // build sms
    throw std::invalid_argument("unknown notifier type");              // fail clearly
}

The caller receives std::unique_ptr<Notifier>, so it can use the object without knowing the exact class. If you later add PushNotifier, most client code does not change.

Key Points / Rules

  • Use Factory when object creation is repeated or conditional.
  • Return a base type when callers should depend on abstraction.
  • Prefer std::unique_ptr for exclusive ownership of created objects.
  • Keep validation inside the factory so unknown requests fail predictably.
  • Do not hide simple constructors behind factories when there is no creation problem.

Common Mistakes

  1. Putting too much logic in the factory. A factory should create objects, not run the whole business workflow.
  2. Returning raw owning pointers. Returning new EmailNotifier makes ownership unclear and risks leaks.
  3. Using Factory for every class. A direct constructor call is clearer when there is only one simple concrete type.
  4. Forgetting a virtual destructor. Base classes used through pointers should have a virtual destructor.

Quick Check

  1. Why does makeNotifier() return std::unique_ptr<Notifier> instead of EmailNotifier?
  2. What code changes when you add a new concrete notifier type?

Viva Answer

Factory is a design pattern that centralizes object creation. It lets client code ask for an abstract role while the factory decides which concrete class to instantiate.