Java 中的工厂实现

问题描述

经过几年的 Python 编码后,我最近为了一个项目转向了 Java。 在使用 Python 时,我有一个漂亮的工厂实现。

# file abstract_product.py
from abc import ABC,abstractmethod
class AbstractProduct(ABC):
    @abstractmethod
    def do_something():
        pass
# file product_factory.py
from abstract_product import AbstractProduct

class ProductFactory:
    def __init__(self):
        self._creators = {}
    
    def get(self,product_name) -> Product:
        if product_name not in self._creators:
            raise ValueError('No valid implementation !')
        return self._creators[product_name]()
    
    def register(self,product_name,product):
        self._creators[product_name] = product

product_factory = ProductFactory()
# file product1.py
from abstract_product import AbstractProduct
from product_factory import product_factory

class Product1(AbstractProduct):
    def do_something():
        # does something
        pass

product_factory.register('product1',Product1)

现在的优势是,如果我有一个新的实现 产品,我所要做的就是

# file product2.py
from abstract_product import AbstractProduct
from product_factory import product_factory

class Product2(AbstractProduct):
    def do_something():
        # does something
        pass

product_factory.register('product2',Product2)

上述方法的优点是:

  • 我的工厂是单身人士。在模块中定义变量确保了这一点。
  • 注册新产品,不包括对现有代码的更改。
  • 如果其他地方必须设置梯子,就不会弄脏!
  • 在自己的模块中注册到工厂的新实现。很干净 :D :D

所有需要知道的客户端代码是上面的 product_factory 和字符串参数,客户端将基于该参数获得 Product 的一些实现。

然而,现在使用 Java,我在想我能做些什么,以接近上述方法的简单性和可扩展性!

注意:

还请提出一些您可能遇到的其他方法,用于可扩展工厂,甚至可能比上述方法更好!

解决方法

这就是我喜欢的方式(以另一个班级为例),

public class MyFactory {
    private Map<String,MyInterface> factoryMap = new HashMap<>();

    @Autowired
    public MyFactory(List<MyInterface> listOfObjectsImplementingMyInterface) {
        for (MyInterface myInterface : listOfObjectsImplementingMyInterface) {
//Get the class annotation value,use it as map's key
            String strategyKey = myInterface.getClass().getAnnotationsByType(Component.class)[0].value();
            factoryMap.put(strategy,myInterface);
            
        }
    }

// To get an instantiation from factory
    public MyInterface getFromFactory(String strategyKey) {
        return factoryMap.get(strategyKey);
    }
}

上面的例子是一个 spring f/w 项目的片段,通过这种方法你可以利用 spring 注释来填充工厂,而不是使用混乱的 if/else/switch 块。上述方法也可以通过自定义注解扩展到其他情况。

,

您的 Python 代码可以很容易地转换为 Java,而且看起来不会“外来”。

// could even be an interface
abstract class Product {
  // ...
  abstract void doSomething();
}

final class ProductFactory {
  // not strictly a singleton,to allow you to create multiple factories
  // your python code allows this too

  private static ProductFactory instance = new ProductFactory();

  public static ProductFactory getInstance() {
    return instance;
  }

  private HashMap<String,Supplier<? extends Product>> creators = new HashMap<>();

  public void register(String productName,Supplier<? extends Product> creator) {
    creators.put(productName,creator);
  }

  public Product get(String productName) {
    Supplier<? extends Product> creator = creators.get(productName);
    if (creator == null) {
      throw new IllegalArgumentException("No valid implementation !");
    }
    return creator.get();
  }
}

class Product1 extends Product {

  @Override
  void doSomething() {

  }
}

注册和获取产品的示例:

ProductFactory.getInstance().register("product1",Product1::new);
System.out.println(ProductFactory.getInstance().get("product1"));
,

在我看来,从 Python 代码到 Java 的严格转换将是下面的片段。表明这对您来说应该更熟悉。

对于简单的应用程序,您可以让工厂使用 static 或使用单例设计模式来确保单个实例。

如果您正在使用某些框架,它很可能提供了一种替代方案,但需要更少的编码和更好的可测试性。

import java.util.HashMap;
import java.util.Map;

// AbstractProduct may better be an 'interface' even
abstract class AbstractProduct {
    abstract void doSomething();
}

class Product1 extends AbstractProduct {
    @Override
    void doSomething() {
        System.out.println("I'm Product ONE (1)");
    }
}

class Product2 extends AbstractProduct {
    @Override
    void doSomething() {
        System.out.println("I'm Product TWO (2)");
    }
}

class ProductFactory {
    private final Map<String,Class<? extends AbstractProduct>> creators;

    ProductFactory() {
        this.creators = new HashMap<>();
    }

    AbstractProduct get(String productName) {
        if (!creators.containsKey(productName)) {
            throw new RuntimeException("No valid implementation !");// <-- better define or use a specific exception
        }
        try {
            return creators.get(productName).newInstance(); // <-- Class.newInstance is deprecated since Java9,check docs for replacement
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e); // <-- deal with it properly
        }
    }

    void register(String productName,Class<? extends AbstractProduct> productClass) {
        creators.put(productName,productClass);
    }
}

public class PythonFactoryThing {

    public static void main(String[] args) {
        ProductFactory productFactory = new ProductFactory();
        productFactory.register("product1",Product1.class);
        productFactory.register("product2",Product2.class);

        productFactory.get("product1").doSomething();
        productFactory.get("product2").doSomething();
        productFactory.get("product3").doSomething(); // <-- throws exception
    }

}


编辑:如果您不想注册单独的位置/文件,您可以使用静态初始化块(某些数据库驱动程序会这样做)。

你必须像这样修改上面的代码:

从你的工厂中制作一个单身人士:

    private ProductFactory() {}// private constructor so nobody else cana instantiate it
    
    public static final ProductFactory INSTANCE = new ProductFactory();

让每个班级都做这样的事情:

class Product2 extends AbstractProduct {

    static {
        ProductFactory.INSTANCE.register("product2",Product2.class);
    }

    @Override
    void doSomething() {
        System.out.println("I'm Product TWO (2)");
    }
}

但要使其工作,您还需要显式加载您想要注册的类。在使用工厂之前在某个地方使用它们,或者通过调用:

Class.forName("Product1");

因为类加载器只加载明确需要的类。我想这在 Python 中等同于在脚本中导入文件。