image 13
Factory Method Pattern & Abstract Factory Method Pattern

Factory Method Pattern

Factory Method Pattern is a type of Creational Pattern that allows you to create objects without specifying their class. In other words, it allows you to create objects without specifying the exact class of object that will be created. It provides an interface for creating objects, but allows subclasses to determine which class to instantiate.

This pattern promotes loose coupling between the client code and the actual object creation process, making it easy to add new types of objects without modifying the client code. In other words, the factory method is responsible for creating an object, but it delegates the actual instantiation to its subclasses. This makes it a very flexible pattern for creating objects in a way that is both efficient and easy to maintain.

Let us understand it by an example. Let’s say you’re building some application regarding animals. It will tell what an animal eat and what sound does it makes. So, you need to create the instances of different animal types (e.g. lion, dog, elephant) based on user input. Rather than creating instances of each animal type directly in your code, you can use the Factory Method Pattern to create a factory class that produces animal objects.

First, you would define an Animal interface that specifies the methods that all animal objects should have, such as makeSound() and eat().

typedef struct Animal {
    void (*makeSound)();
    void (*eat)();
} Animal;

Next, you would define concrete classes that implement the Animal interface, such as Lion, dog, and Elephant. e.g. for Lion it will be implemented as below

typedef struct Lion {
    Animal animal;
} Lion;

void lionMakeSound() {
    printf("Roar\n");
}

void lionEat() {
    printf("Eating meat\n");
}

Lion* createLion() {
    Lion* lion = (Lion*) malloc(sizeof(Lion));
    lion->animal.makeSound = lionMakeSound;
    lion->animal.eat = lionEat;
    return lion;
}

// Similar functions for dog and Elephant...

Finally, you would create a factory class that uses the Factory Method Pattern to create animal objects based on user input:

typedef struct AnimalFactory {
    Animal* (*createAnimal)(const char* type);
} AnimalFactory;

Animal* createAnimal(const char* type) {
    if (strcmp(type, "lion") == 0) {
        return (Animal*) createLion();
    } else if (strcmp(type, "tiger") == 0) {
        return (Animal*) createTiger();
    } else if (strcmp(type, "elephant") == 0) {
        return (Animal*) createElephant();
    } else {
        return NULL;
    }
}

Now, when you need to create an animal object, you can simply call the createAnimal() method on the factory object and pass in the desired animal type:

AnimalFactory* factory = (AnimalFactory*) malloc(sizeof(AnimalFactory));
factory->createAnimal = createAnimal;

Animal* myAnimal = factory->createAnimal("lion");
myAnimal->makeSound(); // Outputs: "Roar"
myAnimal->eat(); // Outputs: "Eating meat"

This way, you can create animal objects without needing to know the details of how they’re implemented, and you can easily add new animal types in the future by creating new concrete classes and updating the factory’s createAnimal() method.

To understand in C++, you can go though below code that is doing the same stuff.

#include <iostream>
#include <string>

// Base Animal class
class Animal {
public:
    virtual void makeSound() = 0;
    virtual void eat() = 0;
};

// Lion class, derived from Animal
class Lion : public Animal {
public:
    void makeSound() {
        std::cout << "The lion roars!" << std::endl;
    }

    void eat() {
        std::cout << "The lion eats meat." << std::endl;
    }
};

// Dog class, derived from Animal
class Dog : public Animal {
public:
    void makeSound() {
        std::cout << "The dog barks!" << std::endl;
    }

    void eat() {
        std::cout << "The dog eats dog food." << std::endl;
    }
};

// Elephant class, derived from Animal
class Elephant : public Animal {
public:
    void makeSound() {
        std::cout << "The elephant trumpets!" << std::endl;
    }

    void eat() {
        std::cout << "The elephant eats leaves." << std::endl;
    }
};

// Factory Method to create Animal objects
class AnimalFactory {
public:
    virtual Animal* createAnimal() = 0;
};

// Lion Factory, derived from AnimalFactory
class LionFactory : public AnimalFactory {
public:
    Animal* createAnimal() {
        return new Lion();
    }
};

// Dog Factory, derived from AnimalFactory
class DogFactory : public AnimalFactory {
public:
    Animal* createAnimal() {
        return new Dog();
    }
};

// Elephant Factory, derived from AnimalFactory
class ElephantFactory : public AnimalFactory {
public:
    Animal* createAnimal() {
        return new Elephant();
    }
};

// Main function to create and use Animal objects
int main() {
    AnimalFactory* factory = new LionFactory(); // can be changed to DogFactory or ElephantFactory to create different animals
    Animal* animal = factory->createAnimal();

    animal->makeSound();
    animal->eat();

    delete animal;
    delete factory;

    return 0;
}

In this example, the base Animal class has two pure virtual functions, makeSound and eat, which must be implemented by any derived classes. The derived classes Lion, Dog, and Elephant each implement these functions in their own way.

The AnimalFactory is an abstract class with one pure virtual function, createAnimal, which must be implemented by any derived factory classes. The derived factory classes LionFactory, DogFactory, and ElephantFactory each implement createAnimal to create a new instance of the appropriate animal class.

Finally, the main function creates an AnimalFactory object and uses it to create an Animal object, which can then be used to call the makeSound and eat functions. The specific AnimalFactory object used can be changed to create different types of animals.

Advantages & Disadvantages

Advantages

  • Provides a way to decouple the code that creates objects from the code that uses them.
  • Enables easy extension of the code to create new objects without changing the code that uses them.
  • Encapsulates the object creation process and hides the details of the process from the client.

Disadvantages

  • Requires the creation of a separate factory class for each product class.
  • Requires the client to have knowledge of the concrete product classes.

Abstract Factory Method 

Abstract Factory Method is a type of Creational Pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. The idea behind the pattern is to abstract the creation of objects so that they can be created without knowing the specific implementation details.

In simpler terms, think of an abstract factory as a factory that creates other factories. The abstract factory defines the interface for creating a family of objects, but the concrete implementation of the objects is left up to the subclasses.

Let us try to understand it using the same example i.e. Animals (Lion, dog and elephant).

#include <iostream>
#include <string>

class Animal {
public:
    virtual std::string makeSound() = 0;
    virtual std::string eat() = 0;
};

class Lion : public Animal {
public:
    std::string makeSound() { return "Roar!"; }
    std::string eat() { return "Lion eats meat"; }
};

class Dog : public Animal {
public:
    std::string makeSound() { return "Woof!"; }
    std::string eat() { return "Dog eats kibble"; }
};

class Elephant : public Animal {
public:
    std::string makeSound() { return "Trumpet!"; }
    std::string eat() { return "Elephant eats hay"; }
};

class AnimalFactory {
public:
    virtual Animal* createAnimal() = 0;
};

class LionFactory : public AnimalFactory {
public:
    Animal* createAnimal() { return new Lion; }
};

class DogFactory : public AnimalFactory {
public:
    Animal* createAnimal() { return new Dog; }
};

class ElephantFactory : public AnimalFactory {
public:
    Animal* createAnimal() { return new Elephant; }
};

int main() {
    AnimalFactory* factory = new LionFactory();
    Animal* animal = factory->createAnimal();
    std::cout << animal->makeSound() << std::endl;
    std::cout << animal->eat() << std::endl;

    delete animal;
    delete factory;
    return 0;
}

In this example, we have an abstract Animal class with two pure virtual functions makeSound() and eat() that are implemented by the concrete classes Lion, Dog, and Elephant. We also have an abstract AnimalFactory class with a pure virtual function createAnimal(). This is the abstract factory that we will use to create animals.

We then have three concrete factory classes LionFactory, DogFactory, and ElephantFactory that each implement the createAnimal() function to create the corresponding animal object.

In the main() function, we create a LionFactory object and use it to create a Lion object using the createAnimal() function. We then call the makeSound() and eat() functions on the Lion object, which outputs “Roar!” and “Lion eats meat”, respectively.

Advantages & Disadvantages

Advantages

  • Provides a way to create families of related objects without specifying their concrete classes.
  • Enables easy extension of the code to create new families of objects without changing the code that uses them.
  • Encapsulates the object creation process and hides the details of the process from the client.

Disadvantages

  • Can be complex to implement as it requires the creation of multiple factories and related product classes.
  • Can result in a large number of related classes, which can be difficult to manage.

Difference between Factory Method Pattern and Abstract Factory Method Pattern

The Factory Method pattern is used to create objects of a single type or family of related types. The Abstract Factory pattern is used to create families of related objects without specifying their concrete classes.

In the Factory Method pattern example, the AnimalFactory class created objects of the Animal class and its derived classes (Lion, Dog, Elephant). The type of object created depended on the input passed to the createAnimal() method.

In the Abstract Factory pattern, the AnimalFactory class is an abstract class or interface that defines a factory for creating a family of related objects. The concrete subclasses of AnimalFactory (such as SafariAnimalFactory and DomesticAnimalFactory in the example) are responsible for creating the concrete objects, such as Lion, Dog, and Elephant.

Reference:

Related topics: