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
interfaceWindowsUIElementsFactory
implementsAbstractUIElementsFactory
MacUIElementsFactory
implementsAbstractUIElementsFactory
TextBox
abstract classMacTextBox
extendsTextBox
WindowsTextBox
extendsTextBox
Button
abstract classMacButton
extendsButton
WindowsButton
extendsButton
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?
CodePudding user response:
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.
CodePudding user response:
I would like to show you an image from
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.