Skip to content

3.10. Factory Design Pattern

lukemartinlogan edited this page Jul 8, 2023 · 6 revisions

The Factory pattern is used to provide a common interface to construct objects that have a common base class. The main benefit is that you can dynamically choose different policies at runtime, instead of forcing a specific policy at compile-time.

Let's say we want to make a factory for I/O schedulers. An I/O scheduler is a policy to decide the order with which I/O requests are sent to a storage device.

3.10.1. The Base Class

First, we define the base class I/O scheduler.

class IoSched {
 public:
  virtual void PushRequest() = 0;
  virtual void PopRequest() = 0;
};

We have two methods: push an I/O request and pop an I/O request.

3.10.2. The Derived Classes

Now we implement some specific I/O scheduling policies. In this example, we provide RoundRobinSched and DeadlineSched.

class RoundRobinSched : public IoSched {
 public:
  void PushRequest() override {}
  void PopRequest() override {}
};

class DeadlineSched : public IoSched {
 public:
  DeadlineSched(int a, int b) {}
  void PushRequest() override {}
  void PopRequest() override {}
};

DeadlineSched takes parameters a & b. RoundRobinSched has no parameters.

3.10.3. The Factory

enum class IoSchedType {
  kRoundRobin,
  kDeadline
};

class IoSchedFactory {
 public:
  static std::unique_ptr<IoSched> Get(IoSchedType type) {
    switch (type) {
      case IoSchedType::kRoundRobin:
        return std::make_unique<RoundRobinSched>();
      case IoSchedType::kDeadline:
        return std::make_unique<DeadlineSched>(0, 1);
    }
  }
};

3.10.4. Usage

Now you can use the factory to construct your object.

int main() {
  auto sched = IoSchedFactory::Get(IoSchedType::kRoundRobin);
  sched->PushRequest();
  sched->PopRequest();
}

3.10.5. Discussion

One of the main benefits of the factory design is that you can change policies dynamically. I/O schedulers in the Linux kernel for example can be changed at any time. Below is an example of using the Factory to dynamically set the I/O scheduling policy.

int main(int argc, char **argv) {
  IoSchedType sched_type = static_cast<IoSchedType>(std::stoi(argv[1]));
  auto sched = IoSchedFactory::Get(sched_type);
  sched->PushRequest();
  sched->PopRequest();
}

Factories do have a performance penalty since they require a memory allocation to use. However, this penalty is almost always insignificant. If a factory matches your use case, then make your code readable first. If it turns out to cause significant performance problems, then fix it later.