The refactored code uses the Simple Factory Pattern. All object creation now happens in LogisticsFactory, so LogisticsService no longer depends on classes like Air or Road.
1. Key Improvements
Separation of Concerns
The Benefit:LogisticsService does not need to know how to create a transport object. It only needs to know how to use one.
The Result: Separate classes handle the business logic (sending) and the creation logic (building the object).
Encapsulation of "New"
The Benefit: Only the Factory uses the new keyword.
The Impact: If the Air constructor changes tomorrow, you fix it in one place (the Factory), not in every class that uses Air.
Error Handling & Validation
The Benefit: The Factory is one place to check the input.
The Safety: For an unknown mode, the Factory throws an IllegalArgumentException. The system never carries on with a missing (null) object.
2. Structural Breakdown
Component
Responsibility
Product Interface (Logistics)
Defines the shared methods every transport type must have.
Concrete Products (Air, Road)
Real classes that implement the interface, each with its own logic.
Factory Class (LogisticsFactory)
Decides which concrete product to create, based on the input.
Client (LogisticsService)
Asks the factory for an object and uses it through the interface.
3. Comparison of "Before" vs "After"
Feature
Before (Hardcoded)
After (Factory Pattern)
Dependency
Depends on Air and Road classes.
Depends on Logistics interface.
Maintenance
High risk; adding a mode can break existing code.
Low risk; changes are isolated to the Factory.
Testing
Hard to replace concrete objects with fakes (mocks).
1package DesignPatterns.CreationalPatterns.FactoryDesignPattern.GoodCode;23/**4 * 1. PRODUCT INTERFACE5 * Defines the common behavior for all transport types.6 */7interface Logistics {8 void send();9}1011/**12 * 2. CONCRETE PRODUCTS13 * Specific implementations of the Logistics interface.14 */15class Road implements Logistics {1617 @Override18 public void send() {19 System.out.println("Sending by road logic");20 }21}2223class Air implements Logistics {2425 @Override26 public void send() {27 System.out.println("Sending by air logic");28 }29}3031/**32 * 3. THE FACTORY33 * Encapsulates the 'new' keyword and instantiation logic.34 * This is the only place where the system decides which class to create.35 */36class LogisticsFactory {3738 public static Logistics getLogistics(String mode) {39 // Benefit: We can use ignoreCase to prevent common string typos40 if (mode.equalsIgnoreCase("Air")) {41 return new Air();42 } else if (mode.equalsIgnoreCase("Road")) {43 return new Road();44 }4546 // Error Handling: Centralized place to handle invalid requests47 throw new IllegalArgumentException("Unknown logistics mode: " + mode);48 }49}5051/**52 * 4. THE SERVICE (Client)53 * Now purely focused on THE USE of the object, not the CREATION.54 * It is decoupled from 'Air' and 'Road' classes.55 */56class LogisticsService {5758 public void send(String mode) {59 /* Dependency Inversion: The service depends on the Logistics interface,60 not the concrete Air/Road implementations.61 */62 Logistics logistics = LogisticsFactory.getLogistics(mode);63 logistics.send();64 }65}6667// Driver Code68class Main {6970 public static void main(String[] args) {71 LogisticsService service = new LogisticsService();72 service.send("Air");73 service.send("Road");74 }75}76