问题描述
经过几年的 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)
上述方法的优点是:
所有需要知道的客户端代码是上面的 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 中等同于在脚本中导入文件。