Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Implementing the Simple Factory Pattern in C++ for Arithmetic Operations

Notes 2

The Simple Factory pattern centralizes object instantiation behind a unified interface, adhering strictly to the Dependency Inversion Principle. High-level modules interact exclusively with abstractions rather than concrete implementations, decoupling client logic from construction details.

1. Abstract Base Interface

Defining a common contract ensures that all derived operations share a consistant API. Protected state variables allow subclasses direct access while preventing external mutation.

class BaseCalculator {
public:
    virtual ~BaseCalculator() = default;

    virtual void assign_operands(double lhs, double rhs) {
        left_val_ = lhs;
        right_val_ = rhs;
    }

    virtual double compute() const = 0;

protected:
    double left_val_ = 0.0;
    double right_val_ = 0.0;
};

2. Concrete Operation Implementations

Each mathematical operation inherits the base contract and provides specific computation logic. The override keyword guarantees signature alignment with the abstract interface.

class AdditionTask : public BaseCalculator {
public:
    double compute() const override {
        return left_val_ + right_val_;
    }
};

class SubtractionTask : public BaseCalculator {
public:
    double compute() const override {
        return left_val_ - right_val_;
    }
};

3. Factory Instantiation Logic

The factory class encapsulates the creation mechanism. By returning a smart pointer, memory management is automated, eliminating manual deallocation and preventing leaks. A conditional structure routes the request to the appropriate concrete class.

#include <memory>
#include <stdexcept>

class CalculatorFactory {
public:
    static std::unique_ptr<BaseCalculator> build(char operator_symbol) {
        switch (operator_symbol) {
            case '+': return std::make_unique<AdditionTask>();
            case '-': return std::make_unique<SubtractionTask>();
            default:  throw std::invalid_argument("Unsupported operator");
        }
    }
};

4. Client Usage

Consumers request an abstraction from the factory and execute operations without awareness of the underlying concrete type. The lifecycle is automatical handled by the smart pointer.

#include <iostream>

void execute_demo() {
    auto calc = CalculatorFactory::build('+');
    calc->assign_operands(15.5, 4.5);
    std::cout << "Computed Result: " << calc->compute() << '\n';
}

Encapsulating instantiation logic isolates client code from construction details. The factory acts as a decoupling layer, enabling runtime selection of behaviors based on configuration or input parameters.

The primary architectural limitation stems from the conditional branching inside the factory method. Introducing new operations requires modifying the existing switch statement, which directly violates the Open-Closed Principle. Systems requiring frequent extension should consider registration-based factories, polymorphic registries, or template metaprogramming to eliminate conditional modification and achieve true extensibility.

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.