The Abstract Factory Design Pattern with Modern C++

The Gang of Four state that the abstract factory design pattern is used to “provide an interface for creating families of related or dependent objects without specifying their concrete classes.”

Abstract factories provide an interface for creating a family of products. By writing code that uses this interface we can decouple our code from the actual factory that creates the products. This allows us to implement a variety of factories that create products meant for different contexts. This decoupling aspect is what makes abstract factories so useful.

In the example below, adapted from riptutorial, we will look at the case of creating a GUI for Windows and Linux environments. In this example, the code is decoupled from the actual products, which means we can easily substitute different factories to get different behaviors. We create two concrete implementations of the GUIFactory class, which itself is composed of factory methods, one for windows operating systems, and another for linux operating systems. We then use the generic interface created by the GUIFactory class to create concrete objects.

Because this is modern C++, we use unique_ptr pretty liberally.

/* abstract_factory_example.cpp */
#include <iostream>
#include <memory>
#include <string>

/* GUIComponent abstract base class */
class GUIComponent {
public:
  virtual ~GUIComponent() = default;
  virtual void draw() const = 0;
};

class Frame  : public GUIComponent {};
class Button : public GUIComponent {};
class Label  : public GUIComponent {};
class ScrollBar : public GUIComponent {};

class LinuxFrame : public Frame {
public:
  void draw() const override {
    std::cout << "I'm a Linux frame" << std::endl;
  }
};

class LinuxButton : public Button {
public:
  void draw() const override {
    std::cout << "I'm a Linux button" << std::endl;
  }
};

class LinuxLabel : public Label {
public:
  void draw() const override {
    std::cout << "I'm a Linux label" << std::endl;
  }
};

class LinuxScrollBar : public ScrollBar {
public:
  void draw() const override {
    std::cout << "I'm a Linux scrollbar" << std::endl;
  }
};

class WindowsFrame : public Frame {
public:
  void draw() const override {
    std::cout << "I'm a Windows frame" << std::endl;
  }
};

class WindowsButton : public Button {
public:
  void draw() const override {
    std::cout << "I'm a Windows button" << std::endl;
  }
};

class WindowsLabel : public Label {
public:
  void draw() const override {
    std::cout << "I'm a Windows label" << std::endl;
  }
};

class WindowsScrollBar : public ScrollBar {
public:
  void draw() const override {
    std::cout << "I'm a windows scrollbar" << std::endl;
  }
};

/* Abstract factory abstract base class
 * Note: abstract factories can also be concrete
 */
class GUIFactory {
public:
  virtual ~GUIFactory() = default;
  /* create_frame factory method */
  virtual std::unique_ptr<Frame> create_frame() = 0;
  /* create_button factory method */
  virtual std::unique_ptr<Button> create_button() = 0;
  /* create_label factory method */
  virtual std::unique_ptr<Label> create_label() = 0;
  /* create_scrollbar factory method */
  virtual std::unique_ptr<ScrollBar> create_scrollbar() = 0;
  /* create static method to select which concrete factory to instantiate */
  static std::unique_ptr<GUIFactory> create(const std::string& type);
};

/* Concrete windows factory */
class WindowsFactory : public GUIFactory {
public:
  std::unique_ptr<Frame> create_frame() override {
    return std::make_unique<WindowsFrame>();
  }
  std::unique_ptr<Button> create_button() override {
    return std::make_unique<WindowsButton>();
  }
  std::unique_ptr<Label> create_label() override {
    return std::make_unique<WindowsLabel>();
  }
  std::unique_ptr<ScrollBar> create_scrollbar() override {
    return std::make_unique<WindowsScrollBar>();
  }
};

/* Concrete Linux factory */
class LinuxFactory : public GUIFactory {
public:
  std::unique_ptr<Frame> create_frame() override {
    return std::make_unique<LinuxFrame>();
  }
  std::unique_ptr<Button> create_button() override {
    return std::make_unique<LinuxButton>();
  }
  std::unique_ptr<Label> create_label() override {
    return std::make_unique<LinuxLabel>();
  }
  std::unique_ptr<ScrollBar> create_scrollbar() override {
    return std::make_unique<LinuxScrollBar>();
  }
};

/* create static method to select which type of factory to use */
std::unique_ptr<GUIFactory> GUIFactory::create(const std::string& type) {
  if (type == "windows") return std::make_unique<WindowsFactory>();
  return std::make_unique<LinuxFactory>();
}

/* build_interface function that takes in an abstract factory as a param*/
void build_interface(GUIFactory& factory) {
  auto frame = factory.create_frame();
  auto button = factory.create_button();
  auto label = factory.create_label();
  auto scrollbar = factory.create_scrollbar();

  frame->draw();
  button->draw();
  label->draw();
  scrollbar->draw();
}

int main(int argc, char *argv[]) {
  if (argc < 2) return 1;
  auto guiFactory = GUIFactory::create(argv[1]);
  build_interface(*guiFactory);
}

We’ll compile this quick with a simple g++ invocation:

g++ -o abstract_factory_example abstract_factory_example.cpp

Then, if we run the executable with command the line input character array ‘linux’:

./abstract_factory_example linux

we get the output:

I'm a Linux frame
I'm a Linux button
I'm a Linux label
I'm a Linux scrollbar

and if we run the executable with the input ‘windows’:

./abstract_factory_example windows

we get the output:

I'm a Windows frame
I'm a Windows button
I'm a Windows label
I'm a windows scrollbar

Designing Loosely Coupled C++ Classes Pt. I

One of the easiest ways to ensure loose coupling between objects in C++ is to use abstract base classes to define an interface, and then use that interface in other classes.

Let’s define a simple FilterInterface abstract base class that defines the interface for subsequent filter classes, which will then be used as a component of an ADC class.

#include <iostream>
#include <vector>

class FilterInterface {
public:
  virtual ~FilterInterface() {}
  virtual void reset() = 0;
  virtual void update(int a, int b) = 0;
  virtual int get_a() = 0;
  virtual int get_b() = 0;

protected:
  int a_{1};
  int b_{1};
};

class AverageFilter : public FilterInterface {
public:
  void reset() override final{
    a_ = 0;
    b_ = 0;
  }

  void update(int a, int b) override final {
    a_ = a;
    b_ = b;
  }

  int get_a() override final {
    return a_;
  }

  int get_b() override final {
    return b_;
  }
};

class ADC {
public:
  ADC(FilterInterface& interface) : interface(interface) {
    interface.reset();
  }

  void process() {
    interface.update(10, 20);
  }

  std::vector<int> get_values() {
    std::vector<int> vec;
    vec.push_back(interface.get_a());
    vec.push_back(interface.get_b());

    return vec;
  }

protected:
  FilterInterface& interface;
};

int main() {
  AverageFilter filter;
  std::cout<<"filter.a = "<<filter.get_a()<<", filter.b = "<<filter.get_b()<<std::endl;
  std::cout<<"Constructing adc object"<<std::endl;
  ADC adc(filter);
  std::vector<int> vec = adc.get_values();
  std::cout<<"adc.interface.a = "<<vec[0]<<", adc.interface.b = "<<vec[1]<<std::endl;
  std::cout<<"calling process method"<<std::endl;
  adc.process();
  vec = adc.get_values();
  std::cout<<"adc.interface.a = "<<vec[0]<<", adc.interface.b = "<<vec[1]<<std::endl;
}

First, we declare the abstract base class FilterInterface by declaring pure virtual functions. We then inherit from it to create the derived class AverageFilter. The class ADC then takes in a reference to something that is FilterInterface like, or at least uses the interface defined by it. This allows us to have the ADC class decoupled from the implementation details of child classes of FilterInterface, and we can pass in a references to other child classes of it. This way, if we decide we need to change the filter that’s used in ADC, and want to use, let’s say some class we called SavitzkyGolayFilter, it’s easy peasy.

If we compile and run the executable above we get the following output:

filter.a = 1, filter.b = 1
Constructing adc object
adc.interface.a = 0, adc.interface.b = 0
calling process method
adc.interface.a = 10, adc.interface.b = 20

Which allows us to see the polymorphic business going on pretty easily.