我目前正在进行一个涉及创建抽象层的项目。该项目的目标是在我可能需要切换到服务器软件的情况下支持服务器软件的多个实现。要抽象的功能列表相当长,所以我想研究一种相当无痛的方法。
其他应用程序将能够与我的项目交互,并进行调用,这些调用最终会传递到我使用的服务器。
问题就在这里。我在这方面没有太多经验,我真的不知道如何让它不成为死亡的三明治。这是一个大致的链条,它应该是什么样子(以及我正在努力实现的目标)。
/*
Software that is dependent on mine
|
Public API layer (called by other software)
|
Abstraction between API and my own internal code (this is the issue)
|
Internal code (this gets replaced per-implementation, as in, each implementation needs its own layer of this, so it's a different package of entirely different classes for each implementation)
|
The software I'm actually using to write this (which is called by the internal code)
*/
抽象层(显然是最中间的一层)是我正在努力构建的。
现在,我只停留在一个愚蠢的方面。我如何才能使抽象层成为一系列之外的东西
public void someMethod() {
if(Implementation.getCurrentImplementation() == Implementation.TYPE1) {
// whatever we need to do for this specific implementation
else {
throw new NotImplementedException();
}
}
(请原谅伪代码;此外,想象同样的情况,但对于开关/情况,因为这可能比每个方法的if链更好)对于每个抽象级别类中的每个方法。
这看起来很初级,但我无法想出一个合乎逻辑的解决方案来解决这个问题。如果我没有解释清楚我的观点,请用我需要详细说明的内容来解释。也许我想错了这整件事?
为什么不使用控制反转?
您有了一组抽象,创建了几个实现,然后将公共api配置为使用其中一个实现。
API受实现继承的一组接口的保护。您可以在以后添加新的实现,而无需修改API代码,甚至可以在运行时进行切换。
我不知道控制反转是依赖注入,还是DI是Ioc的一种形式,但。。。只是您从组件中删除了依赖关系管理的责任。
在这里,你将有
- API层(客户端使用的接口)
- 实现(无限)
- wrapper(通过引入impl来实现IoC)
API层:
// my-api.jar
public interface MyAPI {
String doSomething();
}
public interface MyAPIFactory {
MyAPI getImplementationOfMyAPI();
}
实现:
// red-my-api.jar
public class RedMyAPI implements MyAPI {
public String doSomething() {
return "red";
}
}
// green-my-api.jar
public class GreenMyAPI implements MyAPI {
public String doSomething() {
return "green";
}
}
// black-my-api.jar
public class BlackMyAPI implements MyAPI {
public String doSomething() {
return "black";
}
}
一些包装器提供了一种配置正确实现的方法。在这里,您可以在工厂中隐藏您的开关案例,或者从配置中加载impl。
// wrapper-my-api.jar
public class NotFunnyMyAPIFactory implements MyAPIFactory {
private Config config;
public MyAPI getImplementationOfMyAPI() {
if (config.implType == GREEN) {
return new GreenMyAPI();
} else if (config.implType == BLACK) {
return new BlackMyAPI();
} else if (config.implType == RED) {
return new RedMyAPI();
} else {
// throw...
}
}
}
public class ReflectionMyAPIFactory implements MyAPIFactory {
private Properties prop;
public MyAPI getImplementationOfMyAPI() {
return (MyAPI) Class.forName(prop.get('myApi.implementation.className'))
}
}
// other possible strategies
工厂允许使用几种策略来加载类。根据解决方案的不同,您只需添加一个新的依赖项并更改配置(并重新加载应用程序…或不重新加载)即可更改实现。
您可能还需要测试性能。
如果使用Spring,则只能在代码中使用接口,并从配置类注入正确的实现(Spring是DI容器)。但不需要使用Spring,您可以直接在Main入口点上执行此操作(从最近的入口点注入)。
- my-api.jar没有依赖项(或者可能有一些指向内部层的依赖项)
- 所有的jar for实现都依赖于my-api.jar和您的内部代码
- 包装器jar依赖于my-api.jar和一些impl jar
因此,客户端加载他想要的jar,使用他想要的工厂或注入impl的配置,然后使用您的代码。这也取决于你如何公开你的api。