Transitioning from Factory Method to Abstract Factory Pattern in Java
Suppose a business originally focused on agricultural goods now wants to expand into the apparel industry. Fruit and clothing items clearly belong to different production lines, so they cannot be manufactured in the same facility. Each production line may offer multiple specific products, yet all factories share the common trait of being production units under the same ownership. To manage this structure efficiently, we can apply the Abstract Factory pattern.
Product Interfaces and Implementations
First, define the Produce interface for agricultural goods:
public interface Produce {
void display();
}
Implement two concrete fruit products:
public class Orange implements Produce {
@Override
public void display() {
System.out.println("Orange crop harvested.");
}
}
public class Banana implements Produce {
@Override
public void display() {
System.out.println("Banana crop harvested.");
}
}
Next, define the Apparel interface for clothing items:
public interface Apparel {
void manufacture();
}
Implement two clothing products:
public class TShirt implements Apparel {
@Override
public void manufacture() {
System.out.println("T-Shirt manufactured.");
}
}
public class Jacket implements Apparel {
@Override
public void manufacture() {
System.out.println("Jacket manufactured.");
}
}
Abstract Factory Definition
Create a base factory class that declares methods for producing both product families:
public abstract class BaseManufacturer {
public abstract Produce createProduce(String type);
public abstract Apparel createApparel(String type);
}
Concrete Factories
The agriculture-focused factory only handles produce:
public class AgricultureFactory extends BaseManufacturer {
@Override
public Produce createProduce(String type) {
if (type == null) return null;
switch (type.toLowerCase()) {
case "orange": return new Orange();
case "banana": return new Banana();
default: return null;
}
}
@Override
public Apparel createApparel(String type) {
return null;
}
}
The clothing-focused factory only handles apparel:
public class GarmentFactory extends BaseManufacturer {
@Override
public Produce createProduce(String type) {
return null;
}
@Override
public Apparel createApparel(String type) {
if (type == null) return null;
switch (type.toLowerCase()) {
case "tshirt": return new TShirt();
case "jacket": return new Jacket();
default: return null;
}
}
}
Factory Producer
A producer class acts as a factory of factories:
public class ManufacturerHub {
public static BaseManufacturer getManufacturer(String category) {
if (category == null) return null;
switch (category.toLowerCase()) {
case "produce": return new AgricultureFactory();
case "apparel": return new GarmentFactory();
default: return null;
}
}
}
Usage Example
public class ManufacturerDemo {
public static void main(String[] args) {
BaseManufacturer agriculture = ManufacturerHub.getManufacturer("produce");
Produce orange = agriculture.createProduce("orange");
orange.display();
Produce banana = agriculture.createProduce("banana");
banana.display();
BaseManufacturer garment = ManufacturerHub.getManufacturer("apparel");
Apparel tshirt = garment.createApparel("tshirt");
tshirt.manufacture();
Apparel jacket = garment.createApparel("jacket");
jacket.manufacture();
}
}
Output:
Orange crop harvested.
Banana crop harvested.
T-Shirt manufactured.
Jacket manufactured.
The Abstract Factory pattern is a creational design pattern that provides a way to encapsulate a group of individual factories with a common goal. It is often described as a factory of factories. Each concrete factory is responsible for creating products of a specific family, while the abstract layer ensures consistency across different factory implementations.
- Advantages: Easy to introduce new product families or extend existing ones. It promotes consistency among products and hides creation logic from the client.
- Disadvantages: Increases system complexity due to additional layers of abstraction, which may affect readability for beginners.