jeudi 29 décembre 2022

Factory DesignPattern with registering the subclasses using static blocks

I am trying to implement Factory design patter, without using reflection. The goal is, as and when subclasses of Product are added to the project, their instances should be available in the objectPool.

The code is as below : Factory Class

public abstract class ProductFactory {
  protected static final Map<String, Supplier<Product>> productSingletonObjPool = new HashMap<>();

  static {
    /*
    //registering the sub_class-types statically. This obviously works fine.
    productSingletonObjPool.put(ProductOne.class.getSimpleName(), ProductOne::getInstance);
      productSingletonObjPool.put(ProductTwo.class.getSimpleName(), ProductTwo::getInstance);
    */
  }

  public static Map<String, Supplier<Product>> getProductSingletonObjPool() {
    return productSingletonObjPool;
  }

  public static void register(Class type, Supplier<Product> supplier) {
    productSingletonObjPool.put(type.getSimpleName(), supplier);
  }

  protected static Product getInstanceOfType(String type) {
    Supplier<Product> productSupplier = productSingletonObjPool.get(type);
    if (productSupplier != null) return productSupplier.get();
    throw new RuntimeException("No such product with name " + type + " exists");
  }
}

Abstract Product Class

public abstract class Product {


  protected static void register(Class type, Supplier<Product> supplier) {
    if (!ProductFactory.getProductSingletonObjPool().containsKey(type.getSimpleName())) {
      ProductFactory.register(type, supplier);
    }
  }

}

Concrete ProductOne

public class ProductOne extends Product {

  static {
    Product.register(ProductOne.class, ProductOne::new);
  }

  private ProductOne() {}

  protected static Product getInstance() {
    return new ProductOne();
  }
}

Concreate ProductTwo

public class ProductTwo extends Product {

  static {
    Product.register(ProductTwo.class, ProductTwo::new);
  }

  private ProductTwo() {}

  protected static Product getInstance() {
    return new ProductTwo();
  }
}

Test Class

@Test
public void testFactory() {
  Product one = ProductFactory.getInstanceOfType("ProductOne");
  System.out.println("Product One : " + one);
  Product two = ProductFactory.getInstanceOfType("ProductTwo");
  System.out.println("Product Two: " + two);
}

Problem : the Map always gets populated with ProductOne. This behaviour is same as captured in this thread: Fill a hashmap in superclass from subclass constructor in Java However, My code is different than the code from this thread, though it encounters the behavior mentioned in there i.e. map always getting populated with one Supplier (in my case, its ProductOne::new).

I would like to know :

  1. The reason behind this behavior
  2. Any solution for this?

I believe the static blocks get executed when classes are loaded. I expected that static blocks of both ProductOne and ProductTwo be executed and have the productSingletonObjPool map populated with their Suppliers. This didn't happen though...map only got populated with ProductOne's supplier, as if, JVM only looked for 'a' class that extended the abstract Product class and then stopped loading additional classes which extend Product class. I also tried to debug this, but breakpoints on static blocks are skipped by debugger.

I looked into these threads, as prompted by stackoverflow, but didn't find solution to my problem :

Aucun commentaire:

Enregistrer un commentaire