Part 1: Factory Method
1. Definition
Definition in Wikipedia is:
So, the Factory Method pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. This design pattern let factory defer subclass to decide instantiation.
2. Where we need factory pattern?
We want to use factory method to help us instantiate objects and encounter the following senerios.
- The instantiation is complex.
- Polymorphism. We don’t know which one to instantiate, we want to use some logic to decide which one to instantiate. There are two cases.
- Objects are created in different ways.
- Factory create different things.
Therefore, the Factory Method pattern solves problem like:
- How can an object be created so that subclasses can redefine which class to instantiate?
- How can a class defer instantiation to subclasses?
Creating an object directly within the class that requires (uses) the objects is inflexible because it commits the class to a particular object and makes it impossible to change the instantiation independently from (without having to change) the class. The Factory Method design pattern describes how to solve such problem:
- Define a seperate operation (factory method) for creating an object.
- Create an object by calling a factory method. This enables writing of subclasses to change the way an object is created (to redefine which class to instantiate).
3. UML
The UML is shown as follows.
In the above UML, the Creator class that requires a Product object doesn’t instantiate the ConcreteProduct class directly. Instead, the Creator refers to a separate factoryMethod() to create a proudce object, which makes the Creator independent of which concrete class is instantiated. Subclasses of Creator can redefine which class to instantiate.
Example
This example is from Wikipedia, click here to check.
A maze game may be played in two modes, one with regular rooms that are only connected with adjacent rooms, and one with magic rooms that allow players to be transported at random.
Roomis the base class for a final product (MagicRoomorOrdinaryRoom).MazeGamedeclares the abstract factory method to produce such a base product.MagicMazeGameandOrdinaryMazeGameare subclasses ofMazeGameimplementing the factory method producing the final produces.
Thus factory methods decouple callers(MazeGame) from the implementation of the concrete classes. This makes the “new” Operator redundant, allows adherence to the Open/closed principle and makes the final product more flexible in the event of change.
CLICK HERE TO SEE THE CODES
public abstract class Room {
abstract void connect(Room room);
}
public class MagicRoom extends Room {
public void connect(Room room) {}
}
public class OrdinaryRoom extends Room {
public void connect(Room room) {}
}
public abstract class MazeGame {
private final List<Room> rooms = new ArrayList<>();
public MazeGame() {
Room room1 = makeRoom();
Room room2 = makeRoom();
room1.connect(room2);
rooms.add(room1);
rooms.add(room2);
}
abstract protected Room makeRoom();
}
public class MagicMazeGame extends MazeGame {
@Override
protected Room makeRoom() {
return new MagicRoom();
}
}
public class OrdinaryMazeGame extends MazeGame {
@Override
protected Room makeRoom() {
return new OrdinaryRoom();
}
}
MazeGame ordinaryGame = new OrdinaryMazeGame();
MazeGame magicGame = new MagicMazeGame();
Part 2: Abstract Factory
1. Definition
Abstract Factory pattern provide an interface for creating families of related or dependent object without specifying their concrete classes. Difference between Factory Method is factory method create single object, abstract factory creates multiple objects.
2. What does abstract factor solve?
The Abstract Factory design pattern solves problems like:
- How can an application be independent of how its objects are created?
- How can a class be independent of how the objects it requires are created?
- How can families of related or dependent objects be created?
3. UML
Image source: wikipedia
The Client class that requires ProductA and ProductB objects doesn’t not instantiate the ProductA1 and ProductB1 classes directly. Instead, the Client refers to the AbstractFactory interface for creating objects, which makes the Client independent of how the objects are created. The Factory1 class implements the AbstractFactory interface by instantiating the ProductA1 and ProdctB1.
The UML sequence diagram shows the run-time interactions: The Client object calls createProductA() on the Factory1 object, which creates and returns a ProductA1 object. Thereafter, the Client calls createProductB() on Factory1, which creates and returns a ProductB1 object.
4. Example
A classic application of abstract factory is the GUIFactory, say we want to design UI elements for different system. The UML is shown as follows. Image source: wikipedia
CLICK HERE TO SEE THE CODES
import java.util.List;
import java.util.Random;
// an existing hierarchy
interface Button {
void paint();
}
class WinButton implements Button {
@Override
public void paint() {
System.out.println("WinButton");
}
}
class OSXButton implements Button {
@Override
public void paint() {
System.out.println("OSXButton");
}
}
public class AbstractFactoryExample {
// abstract the way to create a button
@FunctionalInterface
interface GUIFactory {
public Button createButton();
}
private static GUIFactory factory(String appearance) {
switch(appearance) {
case "osx":
return OSXButton::new;
case "win":
return WinButton::new;
default:
throw new IllegalArgumentException("unknown " + appearance);
}
}
public static void main(final String[] arguments) {
// This is just for the sake of testing this program,
// and doesn't have to do with the Abstract Factory pattern.
var randomAppearance = List.of("osx", "win").get(new Random().nextInt(2));
// get the button factory for an appearance
var factory = factory(randomAppearance);
// use the factory to create the button
var button = factory.createButton();
button.paint();
}
}