2

The abstract factory pattern is useful when we have families of related classes, and we want to instantiate them without relying on the implementation. However, what's wrong with using the factory method pattern in such a situation?

Let's say that we want to construct cross-platform UI elements, e.g. TextBox and Button for Windows and macOS and treat them abstractly. This is the typical situation in which we use the abstract factory pattern, and we can do so by defining the following:

  • AbstractUIElementsFactory interface
  • WindowsUIElementsFactory implements AbstractUIElementsFactory
  • MacUIElementsFactory implements AbstractUIElementsFactory
  • TextBox abstract class
    • MacTextBox extends TextBox
    • WindowsTextBox extends TextBox
  • Button abstract class
    • MacButton extends Button
    • WindowsButton extends Button

and the application would decide which concrete factory to create (based on some OS discovery mechanism) and pass it to a UIApplication class, which instantiates a TextBox and a Button, and calls display on them (which are abstract methods that simply return a String).

The code for this situation:

package abstractFactory;

abstract class Button {
    public abstract void display();
}

class MacButton extends Button {
    public void display() {
        System.out.println("macButton");
    }
}

class WindowsButton extends Button {

    @Override
    public void display() {
        System.out.println("winButton");
    }
}

abstract class TextBox {
    public abstract void display();
}


class MacTextBox extends TextBox {

    @Override
    public void display() {
        System.out.println("macTextBox");
    }
}

class WinTextBox extends TextBox {

    @Override
    public void display() {
        System.out.println("winTextBox");
    }
}

interface UICreatorAbstractFactory {
    Button getButton();
    TextBox getTextBox();
}

class MacFactory implements UICreatorAbstractFactory {

    @Override
    public Button getButton() {
        return new MacButton();
    }

    @Override
    public TextBox getTextBox() {
        return new MacTextBox();
    }
}

class WindowsFactory implements UICreatorAbstractFactory {

    @Override
    public Button getButton() {
        return new WindowsButton();
    }

    @Override
    public TextBox getTextBox() {
        return new WinTextBox();
    }
}

class UIApplication {
    private UICreatorAbstractFactory factory;

    UIApplication(UICreatorAbstractFactory _factory) {
        factory = _factory;
    }

    public void displayUI() {
        factory.getButton().display();
        factory.getTextBox().display();
    }
}

public class Main {
    public static void main(String[] args) {
        new UIApplication(new MacFactory()).displayUI();
    }
}

This implementation allows us to get UI elements transparently from factory implementations and also UI elements implementations, which is largely why we would use the pattern.

Using the same TextBox, Button, and their derivatives, we can have a factory method implementation with two factory methods in the creator, UICreator, each of which returns an abstract UI element. And we derive the creator and make two specializations WindowsUICreator, and MacUICreator, and each of which returns the appropriate concrete UI element, as follows:

abstract class UICreator {

    public void displayUI() {
        getButton().display();
        getTextBox().display();
    }

    protected abstract Button getButton();
    protected abstract TextBox getTextBox();

}

class WindowsUICreator extends UICreator {

    @Override
    protected Button getButton() {
        return new WindowsButton();
    }

    @Override
    protected TextBox getTextBox() {
        return new WinTextBox();
    }
}

class MacUICreator extends UICreator {

    @Override
    protected Button getButton() {
        return new MacButton();
    }

    @Override
    protected TextBox getTextBox() {
        return new MacTextBox();
    }
}

public class Main {

    public static void main(String[] args) {
        new MacUICreator().displayUI();
    }
}

What are the downsides of this design? I believe it provides the needed decoupling by not having to deal with any concrete classes, and also provides the proper extensibility in the sense that we can introduce new UI elements and give them new factory methods, or newly supported OSs and implement concrete creators for them. And if we can use the factory method pattern in the exact situation the abstract factory pattern was designed for, I don't understand why do we have it at all?

Tortellini Teusday
  • 1,335
  • 1
  • 12
  • 21
  • 1
    You might be confused about what the factory method pattern is. Both of your examples are identical, and they're both abstract factories. That is, `UICreatorAbstractFactory` defines an interface for creating a family of objects (buttons and text boxes). `UICreator` does the *exact* same thing; it defines an interface for creating the exact same family of objects. The fact that it's an "abstract class" instead of an "interface" and includes an extra method is irrelevant. A factory method, in contrast, is supposed to create exactly *one* type of object, not a family of them. – Alexander Guyer Feb 11 '22 at 17:02
  • @AlexanderGuyer So, effectively, we can say that "multiple factory methods" are not allowed in the factory method pattern? – Tortellini Teusday Feb 11 '22 at 17:07
  • 1
    More specifically, we can say that "multiple factory methods" is the definition of an abstract factory. The *intention* of an abstract factory is to define an interface for constructing a family of related objects, but the actual appearance of an abstract factory in practice is just multiple factory methods present in one interface. – Alexander Guyer Feb 11 '22 at 17:09
  • I think this question is another way of asking [What are the differences between Abstract Factory and Factory design patterns?](https://stackoverflow.com/q/5739611/1371329) The top four answers in that thread are all high quality, with the fourth being my own. Note the comment above from @AlexanderGuyer perpetuates the all-too-common misconception that Abstract Factory is nothing more than a factory of factories. – jaco0646 Feb 12 '22 at 17:34
  • @jaco0646 Perhaps I should've been more clear. I didn't claim that an abstract factory is a factory of factories at all. Rather, I stated that an abstract factory almost always presents as a single class / interface with multiple "factory methods" in it (one per type of object in the family of related object types). The top and accepted answer in your own reference is quoted saying the *exact* same thing: "The abstract factory is an object that has multiple factory methods on it". Though the distinction that a factory method truly is just a method, rather than an object, is important. – Alexander Guyer Feb 12 '22 at 21:34
  • i had the same doubt, and none of the answer really answers the question. Were you able to find an answer @TortelliniTeusday ? – manjy Jul 11 '23 at 12:28

2 Answers2

1

I would like to show you an image from Saurav Satpathy's blog here which quickly can explain why you want abstract factory over factory method at times.

enter image description here

The argument for dependency injection and collection of related objects makes a lot of sense and here is a coded example by a great creator The Refactoring Guru on Abstract Factory and here is his example on factory method. The main difference between the examples in my opinion is the abstract factory better depicts the complexity of factories that create multiple types of objects. Additionally, it effectively divides the code in more classes, making each class simpler to understand (but creating more classes in total, of course).

Keep in mind this is not a very in depth analysis as of now, I want to see other people's opinions on the matter and give it some time to think for myself. I may come back in a couple of days with an edit (currently a bit busy, but I sneaked a quick opinion for you)

Edit #1 Inheritance

"Favor object composition over class inheritance. Inheritance breaks encapsulation, implement abstract classes, do not inherit concrete classes! - The Gang of Four on Design Patterns"

So object inheritance if you read the GoF's book: "Design Patterns Elements of Reusable Object-Oriented Software" is discouraged, especially when systems become more and more complex or higher in scope. Edit influenced by @FelipeLlinares great point indeed.

1

They are both about creating new objects but the factory method is used to create one product only while the Abstract Factory is about creating families of related or dependent products. In the Abstract Factory pattern, a class delegates the responsibility of object instantiation to another object via composition, whereas the Factory Method pattern uses inheritance and relies on a subclass to handle the desired object instantiation.