我目前是一个项目的一部分,其中有这样的界面:
public interface RepositoryOperation {
public OperationResult execute(Map<RepOpParam, Object> params);
}
这个界面有大约100个实施者。
要致电实施者,需要执行以下操作:
final Map<RepOpParam, Object> opParams = new HashMap<RepOpParam, Object>();
opParams.put(ParamName.NAME1, val1);
opParams.put(ParamName.NAME2, val2);
现在我认为使用<Something, Object>
通用声明的任何内容显然都有问题。
目前,这导致OperationImpl
的调用者必须实际读取操作的代码,以便知道如何构建参数映射。 (这甚至不是最严重的问题,但我不想引用它们,因为它们非常明显)
经过一番讨论后,我设法说服我的同事让我做一些重构。
在我看来,最简单的“修复”就是改变界面:
public interface RepositoryOperation {
public OperationResult execute(OperationParam param);
}
在所有具体操作将定义(扩展)他们自己的OperationParam之后,所有人都可以看到所需的参数。 (这是做恕我直言的'正常方式')
因为我看到它,因为接口实现者非常多,我有几个选择:
尝试更改界面并重写所有操作调用以使用对象而不是地图。这似乎是最干净的,但我认为,由于操作很多,在实践中可能需要做太多工作。 (可能需要约2周的测试)
在界面中添加其他方法,如下所示:
public interface RepositoryOperation {
public OperationResult execute(Map<String, Object> params);
public OperationResult execute(OperationParam params);
}
并在功能实现期间遇到它们时修复地图调用。
与之共存(请不要!)。
所以我的问题是。
有没有人看到更好的方法来“修复”地图,如果你这样做,你会用方法1或2修复它们或根本不修复它们。
修改 谢谢你的答案。如果可以的话,我会接受Max和Riduidel的答案,但是因为我不能再向Riduidel倾斜了。
答案 0 :(得分:2)
我可以看到第三种方式。
您有一张由<RepOpParam, Object>
组成的地图。如果我理解正确,那么困扰你的是没有类型检查的事实。显然,这并不理想。但是,可以将类型检查问题从整个参数(您的OperationParam
)移动到单个RepOpParam
。让我解释一下。
假设您的RepOpParam
界面(目前看起来像tagging interface)被修改为:
public interface RepOpParam<Value> {
public Value getValue(Map<RepOpParam, Object> parameters);
}
然后,您可以通过将旧调用替换为
来更新现代代码String myName = (String) params.get(ParamName.NAME1);
新调用
String myName = ParamName.NAME1.getValue(params);
明显的附带优势是您现在可以为参数设置默认值,隐藏在其定义中。
然而,我已经明确表示,第三种方式只不过是将第二种方式的两种操作合并为一种方式,尊重旧代码原型,同时在其中添加新功能。因此,我会亲自走第一条路,并使用现代对象重写所有“东西”(另外,考虑一下配置库,这可能会引导你找到有趣的问题)。答案 1 :(得分:1)
首先,我认为界面并不完美。你可以添加一些泛型来使它更漂亮:
public interface RepositoryOperation<P extends OperationParam, R extends OperationResult> {
public R execute(T params);
}
现在,我们需要一些向后兼容性代码。我会选择这个:
//We are using basic types for deprecated operations
public abstract class DeprecatedRepositoryOperation implements RepositoryOperation<OperationParam, OperationResult> {
//Each of our operations will need to implement this method
public abstract OperationResult execute(Map<String, Object> params);
//This will be the method that we can call from the outside
public OperationResult execute(OperationParam params) {
Map<String, Object> paramMap = getMapFromObj(params);
return execute(paramMap);
}
}
以下是旧操作的外观:
public class SomeOldOperation extends DeprecatedRepositoryOperation {
public OperationResult execute(Map<String, Object> params) {
//Same old code as was here before. Nothing changes
}
}
新操作会更漂亮:
public class DeleteOperation implements RepositoryOperation<DeleteParam, DeleteResult> {
public DeleteResult execute(DeleteParam param) {
database.delete(param.getID());
...
}
}
但是调用代码现在可以使用这两个函数(代码示例):
String operationName = getOperationName(); //="Delete"
Map<String, RepositoryOperation> operationMap = getOperations(); //=List of all operations
OperationParam param = getParam(); //=DeleteParam
operationMap.execute(param);
如果操作是旧的 - 它将使用DeprecatedRepositoryOperation中的转换器方法。
如果操作是新操作 - 它将使用新的public R execute(T params)
函数。
答案 2 :(得分:0)
听起来你有一种不必要的,误入歧途的抽象。任何时候我看到一个接口都有一个方法,我认为策略模式或动作模式,取决于你是否在运行时做出决定。
清理代码的一种方法是让每个RepositoryOperation实现都有一个构造函数,该构造函数接受execute方法正确运行所需的特定参数。这样,地图中的Object值就不会乱码了。
如果你想保留执行方法签名,你可能可以使用泛型来推测Map的值的更严格的界限。