Abstract Factory is a creational design pattern that provides an interface for creating families of related objects (products) without specifying their concrete classes. In other words, it is a hierarchy of classes that encapsulates many possible “platforms” and the construction of a suite of “products”.
Suppose we are managing the visual appearance of an application (say a text editor).
We declare an interface for each product: BgColor for the bg color and Text for the text. Then we declare subclasses for each family of products: LightText and LightBgcolor for the Light theme and DarkText and DarkBgcolor for the dark theme.
BgColor interface and concrete products
interface BgColor {
// Operations for bgcolor
}
class LightBgColor implements BgColor {
// Operations for light-theme bgcolor
}
class DarkBgColor implements BgColor {
// Operations for dark-theme bgcolor
}
Text interface and concrete products
interface Text {
// Operations for text
}
class LightText implements Text {
// Operations for light-theme text
}
class DarkText implements Text {
// Operations for dark-theme text
}
Now we define code for the instantiation of each family of products inside the Client class. To do this, we define a constructor inside the Client class, which is parameterized based on the theme. For the light theme, we instantiate light theme-based products, and likewise for the dark theme.
import java.util.Scanner;
class Client {
private BgColor bgColor;
private Text text;
public Client(String theme) {
if (theme.equals("Light")) {
bgColor = new LightBgColor();
text = new LightText();
}
else {
bgColor = new DarkBgColor();
text = new DarkText();
}
}
// Operations using bgColor and text
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter the theme - Light or Dark: ");
String inputTheme = scanner.nextLine();
Client client = new Client(inputTheme);
// Use the client object to perform operations using bgColor and text
}
}
There are some drawbacks to this approach:
To overcome these drawbacks, we use an abstract factory design pattern.
Abstract and Concrete products of BgColor
abstract class BgColor {
public abstract String setBg();
}
class LightBgColor extends BgColor {
@Override
public String setBg() {
return "light.";
}
}
class DarkBgColor extends BgColor {
@Override
public String setBg() {
return "dark.";
}
}
Note: All products should be able to interact with each other! For this, the abstract product class for Text will use a method for interaction between bg color and text products.
Abstract and concrete products of Text
abstract class Text {
public abstract String setText();
public abstract String interact(BgColor interactor);
}
class LightText extends Text {
@Override
public String setText() {
return "text-light";
}
@Override
public String interact(BgColor interactor) {
String result = interactor.setBg();
return "text-light interacted with " + result;
}
}
class DarkText extends Text {
@Override
public String setText() {
return "text-dark";
}
@Override
public String interact(BgColor interactor) {
String result = interactor.setBg();
return "text-dark interacted with " + result;
}
}
Abstract Factory (ThemeFactory) provides methods to create each distinct product and concrete factories (LightFactory and DarkFactory) implement these methods to produce appropriate products.
interface ThemeFactory {
BgColor createBg();
Text createText();
}
class LightFactory implements ThemeFactory {
@Override
public BgColor createBg() {
return new LightBgColor();
}
@Override
public Text createText() {
return new LightText();
}
}
class DarkFactory implements ThemeFactory {
@Override
public BgColor createBg() {
return new DarkBgColor();
}
@Override
public Text createText() {
return new DarkText();
}
}
The client uses only the ThemeFactory, BgColor, and Text to instantiate proper products.
class Client {
private ThemeFactory factory;
public Client(ThemeFactory factory) {
this.factory = factory;
}
public void run() {
BgColor bgColor = factory.createBg();
Text text = factory.createText();
System.out.println(bgColor.setBg() + " " + text.setText());
System.out.println(text.interact(bgColor));
}
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter the theme - Light or Dark: ");
String inputTheme = scanner.nextLine();
ThemeFactory themeFactory;
if (inputTheme.equals("Light"))
themeFactory = new LightFactory();
else if (inputTheme.equals("Dark"))
themeFactory = new DarkFactory();
else {
System.out.println("ERROR");
return;
}
Client client = new Client(themeFactory);
client.run();
}
}
Abstract Factory is a creational design pattern that should be used when there are multiple families of products and all the instantiated products should belong to a single family. The responsibility of correctly instantiating the products is not the user's concern.
We also observed the issues caused by the naive approach of hard-coding the instantiation of various products based on their families, and we solved them by using the Abstract Factory pattern.
Thanks to Ankit Nishad for his contribution in creating the first version of this content. If you have any queries or feedback, please write us at contact@enjoyalgorithms.com. Enjoy learning, Enjoy oops!