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.
Room
is the base class for a final product (MagicRoom
orOrdinaryRoom
).MazeGame
declares the abstract factory method to produce such a base product.MagicMazeGame
andOrdinaryMazeGame
are subclasses ofMazeGame
implementing 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: wikipediaThe 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();
}
}