不确定标题是否正确。我是Java的新手,试图弄清楚如何让一个班级使用不同的服务"。假设我有一个APIRequest类,这个类需要能够根据需要使用不同的API。例。我需要运送一个包,如果包低于32OZ我需要使用Endicia,否则我需要使用FedEx。我有2"服务"类FedexRequest和EndiciaRequest。我试图允许APIRequest类使用任何一个,具体取决于包的重量。我创建了一个名为APIService的类,它有一个名为getService的静态方法。它只是创建一个字符串名称的地图 - >请求类如此...
public class APIService {
private static Map<String, Object> services = new HashMap<>();
private static final Map<String, String> availableServices = new HashMap() {{
put("fedex", "FedexRequest");
put("endicia", "EndiciaRequest");
}};
public static Object getService(String type) {
if(services.containsKey(type)) {
return services.get(type);
}
return null;
}
static {
for(Map.Entry<String, String> serv : availableServices.entrySet()) {
try {
Class<?> cls = Class.forName(serv.getValue());
services.put(serv.getKey(), cls.newInstance());
} catch(Exception e) {
services.put(serv.getKey(), new Class[1]);
}
}
}
}
所以现在我可以调用APIService.getService(&#34; fedex&#34;);但是我很难在APIRequest课程中弄清楚如何使用它,因为我需要做类似......
this.service = (FedexRequest) APIService.getService("fedex");
//or
this.service = (EndiciaRequest) APIService.getService("endicia);
但这打破了等式的整个动态部分,如果我以后需要添加其他服务怎么办? 我尝试让FedexRequest和EndiciaRequest实现Request接口,然后使用
this.service = (Request) APIService.getService("fedex");
但是这给了我一个Java.lang.Class错误,说它无法转换为Request。我假设它是因为Request是一个接口,所以你不能在实现类上使用cls.newInstance()然后转换为接口。
我真的迷失了如何允许我的APIRequest类使用FedexRequest或EndiciaRequest,而没有专门使用类型转换,因此它可以是动态的,我们可以在以后添加服务而无需重新编码整个事情。我来自PHP,这将非常简单,因为您不必明确定义类型。任何帮助将不胜感激。谢谢。
答案 0 :(得分:3)
如果我是你,我会做以下事情:
这是Service接口的实现:
public interface Service {
public void performAction();
//other common functions...
}
对APIService类的一个小修改:
public class APIService {
private static Map<String, Service> services = new HashMap<>();
private static final Map<String, String> availableServices = new HashMap() {{
put("fedex", "com.finity.shipping.api.fedexapi.FedexRequest");
put("endicia", "com.finity.shipping.api.endiciaapi.EndiciaRequest");
}};
public static Service getService(String type) {
return services.get(type);
}
static {
for(Map.Entry<String, String> serv : availableServices.entrySet()) {
try {
Class<?> cls = Class.forName(serv.getValue());
services.put(serv.getKey(), cls.newInstance());
} catch(Exception e) {
services.put(serv.getKey(), new Class[1]);
}
}
}
}
每当您需要将服务添加到您的应用程序时,只需实现服务界面:
public class FedexRequest implements Service {
public void performAction() {
//do something
}
}
最后在您的班级中使用this.service
:
Service service;
...
this.service = APIService.getService("fedex");
this.service.performAction();
答案 1 :(得分:0)
Pooya解决方案很好。
我会添加一些东西。您使用一些字符串来表示可打字的内容:常量和类。使用反射来初始化一个工厂,你只处理一些字符串中的类(例如com.finity.shipping.api.fedexapi.FedexRequest
)并且属于你自己的项目的类似乎是一个开销。
如果您的工厂不知道它将实例化哪些类,使用反射是有意义的。但似乎并非如此。
此外,使用枚举可以使FEDEX和ENDICIA保持不变。它允许输入它们并避免不良的suprpises。
我们希望您的工厂更简单。这是一个例子:
public class APIService {
public static enum TypeRequest{
FEDEX, ENDICIA;
}
private static Map<String, Service> services = new HashMap<>();
static {
services.put(FEDEX, new FedexRequest());
services.put(ENDICIA, new EndiciaRequest());
}
public static Service getService(TypeRequest typeRequest) {
return services.get(typeRequest);
}
}