重构建议:映射到POJO

时间:2010-12-14 14:29:58

标签: java dictionary architecture refactoring

我目前是一个项目的一部分,其中有这样的界面:

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之后,所有人都可以看到所需的参数。 (这是做恕我直言的'正常方式')

因为我看到它,因为接口实现者非常多,我有几个选择:

  1. 尝试更改界面并重写所有操作调用以使用对象而不是地图。这似乎是最干净的,但我认为,由于操作很多,在实践中可能需要做太多工作。 (可能需要约2周的测试)

  2. 在界面中添加其他方法,如下所示:

    public interface RepositoryOperation {
        public OperationResult execute(Map<String, Object> params);
        public OperationResult execute(OperationParam params);  
    }
    

    并在功能实现期间遇到它们时修复地图调用。

  3. 与之共存(请不要!)。

  4. 所以我的问题是。

    有没有人看到更好的方法来“修复”地图,如果你这样做,你会用方法1或2修复它们或根本不修复它们。

    修改 谢谢你的答案。如果可以的话,我会接受Max和Riduidel的答案,但是因为我不能再向Riduidel倾斜了。

3 个答案:

答案 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的值的更严格的界限。