Skip to content

Design Pattern: Simple Factory (Creational)

Creating objects without naming the concrete class

Builds on Interfaces and OCP - The Open/Closed Principle

Stage 3core1 min read

1. Tight Coupling

LogisticsService depends directly on the concrete classes Air and Road.

  • The Issue: The high-level service (the one that runs the delivery) knows exactly how to build the low-level transport objects.
  • The Impact: If you rename a class, change a constructor, or delete a transport mode, you must also edit LogisticsService.

2. Violation of the Open/Closed Principle (OCP)

Code should be open for extension (you can add to it) but closed for modification (you do not edit what already works).

  • The Issue: Each new transport mode (e.g., Sea, Rail, Drone) means opening LogisticsService.java and adding another else-if block.
  • The Impact: Every new delivery option puts your core service logic at risk of breaking.

3. "God Method" Logic

The send(String mode) method does two separate jobs. This breaks the Single Responsibility Principle, which says a unit of code should have one job.

  • Responsibility 1: Deciding which object to create (Creation Logic).
  • Responsibility 2: Executing the business logic (logistics.send()).
  • The Impact: As the application grows, this method turns into a huge block of conditions that is hard to unit test.

4. Brittle String Comparisons

The code picks the class with raw string comparisons like mode.equals("Air").

  • The Issue: A small typo (e.g., "air" vs "Air") or a changed naming style makes the request miss every branch. The if-else has no final else, so the service silently does nothing and no error is raised.
  • The Impact: The compiler cannot check that the requested mode exists or is handled.

Summary

The new keyword is useful, but inside business logic it ties your code to one concrete class (a "Concrete Dependency"). The Factory Pattern (here a Simple Factory) fixes this by moving new out of the service and into a dedicated factory class.

In this topic

Before code1 file

BadCode/Main.java62 linesGitHub
package DesignPatterns.CreationalPatterns.FactoryDesignPattern.BadCode;// Logistics Interfaceinterface Logistics {    void send();}// Concrete implementation 1class Road implements Logistics {    @Override    public void send() {        System.out.println("Sending by road logic");    }}// Concrete implementation 2class Air implements Logistics {    @Override    public void send() {        System.out.println("Sending by air logic");    }}/** * PROBLEM: The "LogisticsService" is doing too much. * It handles BOTH the delivery logic AND the creation logic. */class LogisticsService {    public void send(String mode) {        // PROBLEM 1: Tight Coupling        // This service is directly dependent on 'Air' and 'Road' classes.        // If you want to delete 'Road' or rename it, this service breaks.        // PROBLEM 2: Violation of Open/Closed Principle        // If we want to add 'Sea' or 'Rail' transport, we MUST modify this        // if-else block. This increases the risk of breaking existing code.        if (mode.equals("Air")) {            Logistics logistics = new Air(); // 'new' keyword here is a code smell            logistics.send();        } else if (mode.equals("Road")) {            Logistics logistics = new Road();            logistics.send();        }        // PROBLEM 3: Lack of Scalability        // As you add 10 more transport types, this method becomes a giant        // "God Method" that is hard to test and maintain.    }}class Main {    public static void main(String[] args) {        LogisticsService service = new LogisticsService();        service.send("Air");        service.send("Road");    }}

Done with this topic?

How well do you know it?

PreviousSingleton Pattern Guide