How it Works

  1. We have JSON file with Location and Name of Factory Class
  2. Using Configuration.java we read the JSON File and read the Configuration
  3. We run TasteFoodFromMenu.java by supplying the JSON file Location as Input
  4. Now by the above method the JAR’s could be built independently and by changing the JSON file we could make changes and deploy the code without restarting the Server

For more detail refer here

Configuration.java

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;

import com.fasterxml.jackson.databind.ObjectMapper;

public class Configuration {

    public static Configuration loadConfiguration(String fileName) throws IOException {    	
    	//Read Name of File
        Path path = FileSystems.getDefault().getPath(fileName);
        
        //Read Contents of File
        String contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
        
        ObjectMapper mapper = new ObjectMapper();
        
        //Map Location and FactoryType
        Configuration config = mapper.readValue(contents, Configuration.class);
        return config;
    }

    private String factoryType;
    private String location;


    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public String getFactoryType() {
        return factoryType;
    }

    public void setFactoryType(String factoryType) {
        this.factoryType = factoryType;
    }
}

TasteFoodFromMenu.java

public class TasteFoodFromMenu {
	  public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
	        Configuration configuration = Configuration.loadConfiguration(args[0]);
	        String location = configuration.getLocation();
	        URL url = new URL(location);
	        URLClassLoader ucl = new URLClassLoader(new URL[]{url});
	        Class<IFoodFactory> cls = (Class<IFoodFactory>) Class.forName(configuration.getFactoryType(), true, ucl);
	        IFoodFactory cameraFactory = cls.newInstance();
	        IFood camera = cameraFactory.prepareFood(); 
	        camera.serveFood();
	    }
}

config.json

{
  "factoryType": "com.mugil.food.ChineseFoodFactory",
  "location": "file:///D:/java/ReadConfFromJSON/lib/FoodMenu.jar"
}

Factory pattern deals with creating objects of a single type. Define an interface or abstract class for creating an object but let the Subclass decide which class to instantiate.

Why Factory Pattern?
Factory pattern was introduced to hide the Complexity of Object creation and to delegate much of Object creation details to Separate Class(Factory Class – LoggerFactory.java) to avoid unnecessary boiler plate code.

When to use factory Pattern?

  1. Instead of using the new operator or a constructor directly, you use a factory method or a factory class that returns an object of the desired type. The factory method or class can handle the logic of choosing the right subclass, setting the initial state, or applying any dependencies or configurations to the object. This way, you can abstract the creation process and make your code more flexible and modular.
  2. Use factory pattern when We don’t know the exact class of object that will be created at runtime.
  3. To promote loose coupling between classes.
  4. It encapsulates the object creation process which makes the creation process without affecting the rest of the code

How it is implemented?

  1. You define an interface or an abstract class that represents the type of objects you want to create.
  2. Implement one or more subclasses that inherit from it. Each subclass represents a specific implementation or variation of the object.
  3. Create a factory method or a factory class that takes some parameters or input and returns an object of the interface or abstract class type.
  4. The factory method or class decides which subclass to instantiate based on the parameters or input, and returns it as the output.
  5. FactoryClass (LoggerFactory.java) with static factory method decides the objects without exposing the instantiation logic to the Client
  6. Class with main() method starts the whole process

Logger.java

public interface Logger {
    public void log();
}

ConsoleLogger.java

public class ConsoleLogger implements Logger {
    @Override
    public void log() {
        System.out.println("Logging to Console System.. . .");
    }
}

DatabaseLogger.java

public class DatabaseLogger implements Logger {
    @Override
    public void log() {
        System.out.println("Logging to Console System.. . .");
    }
}

FileLogger.java

public class FileLogger implements Logger {
    @Override
    public void log() {
        System.out.println("Logging to txt File... .");
    }
}

LoggerFactory.java

public class LoggerFactory {
    public enum LoggerType {
        DATABASE, FILE, CONSOLE;
    }

    private LoggerFactory() {
    }

    public static Logger getLogger(LoggerType loggerType) {
        Logger logger;

        switch (loggerType) {
            case DATABASE:
                logger = new DatabaseLogger();
                break;
            case CONSOLE:
                logger = new ConsoleLogger();
                break;
            case FILE:
                logger = new ConsoleLogger();
                break;
            default:
                logger = new FileLogger();
        }
        return logger;
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        Logger objLogger = LoggerFactory.getLogger(LoggerFactory.LoggerType.CONSOLE);
        objLogger.log();
    }
}