如何避免类的所有方法常见的重复代码?

时间:2015-07-22 12:43:59

标签: java dao duplicate-removal

在我们的应用程序中,在DAO层中,每个方法都遵循几乎相同的顺序:

public List getSomeValue(String[] parameters) {
    try {
        //Get connection from pool
        //Execute procedure
        //Process resultset
    } catch (SomeException e) {
        //Error handling mechanism
    } finally {
        //Release connection
    }

    return someResult;  
}

上面代码中的所有注释行都描述了我们正在进行的操作。

现在,除了处理结果集部分之外,一切都几乎完全相同。

我的问题是,我们可以实现某种设计,以便我们不必在每种方法中反复编写相同的代码吗?所以,我们只需编写结果集处理部分。

实用方法已经到位。但是,我们必须以完全相同的顺序在每个程序中调用它们。我们可以有一些东西,以便自动调用这些预定义的方法,我们只需要编写不同的部分吗?我甚至不确定这是否可能。

注意:我们不能使用任何像Hibernate这样的ORM工具。

4 个答案:

答案 0 :(得分:1)

为什么不传入知道如何将mapper转换为您期望的元素列表的ResultSet。例如:

public List<T> getSomeValue(String[] parameters, ResultSetMapper<T> mapper) {
    try {
        //Get connection from pool
        //Execute procedure
        //Process resultset
        return mapper.convert(resultSet);  
    } catch (SomeException e) {
        //Error handling mechanism
    } finally {
        //Release connection
    }
}

interface RowMapper<T> {
    List<T> convert(ResultSet resultSet);
}

您传递了不同的mapper实现,而getSomeValue方法将保持不变,因此您只需要实现一次。

如果您在项目中使用Spring,则可以使用其ResultSetExtractorRowMapper类来执行类似操作。

答案 1 :(得分:1)

从java 8开始,你可以将方法重构为类似的东西;

Object doSomething(){
    return doInTransaction(trans -> {
        return trans.load();
    });
}

InTrans

的位置
public interface InTrans<T> {
    T call(Transaction transaction);
}

doInTransaction

<T> T doInTransaction(InTrans<T> callable){
    try {
        Connection connection = ...
        return callable.call(connection));
    } catch (SomeException e) {
        //Error handling mechanism
    } finally {
        //Release connection
    }

}

或者,您可以使用Spring

中的声明性事务

答案 2 :(得分:1)

这使用talex在他的回答中提出的相同原则,除了对于你正在寻找的东西,我相信你想要的是使用Consumer<T>功能界面(或其他类似的东西)接口)使用ResultSet作为输入。

基本上,你所有的连接提取,过程执行,结果集的循环和异常处理都保留在一个方法中而不重复。当您调用该方法时,您将传入将处理每一行的代码。

看起来像这样:

public void callingCode() {
    List<String> someList = new ArrayList<>();
    performQuery(
            "SELECT * FROM ...",
            new String[]{"param1", "param2"},
            rs -> {
                try {
                    // process your row here.
                    someList.add(rs.getString("somecolumn"));
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            });
}

public void performQuery(String query, String[] parameters, Consumer<ResultSet> processRow) {
    try {
        //Get connection from pool
        //Execute procedure
        ResultSet rs = null; // pretend this comes from the procedure call.
        //Process resultset
        while (rs.next()) {
            processRow.accept(rs);
        }
    } catch (Exception e) {
        //Error handling mechanism
    } finally {
        //Release resources
    }
}

修改

Consumer<T>功能接口令人烦恼的是方法签名不允许任何已检查的异常,因此需要明确处理SQLException(在上面的代码中,你可以看到我被迫将SQLException包裹在RuntimeException中。为了避免这种烦恼,您可以选择不使用内置的Consumer<T>功能界面,而是创建自己的包含throws SQLException作为方法签名的一部分。

ResultSetConsumer界面:

public interface ResultSetConsumer {
    void processRow(ResultSet rs) throws SQLException;
}

调整后的代码:

public void callingCode() {
    List<String> someList = new ArrayList<>();
    performQuery(
            "SELECT * FROM ...",
            new String[]{"param1", "param2"},
            rs -> {
                // process your row here.
                someList.add(rs.getString("somecolumn"));
            });
}

public void performQuery(String query, String[] parameters, ResultSetConsumer rsConsumer) {
    try {
        //Get connection from pool
        //Execute procedure
        ResultSet rs = null; // pretend this comes from the procedure call.
        //Process resultset
        while (rs.next()) {
            rsConsumer.processRow(rs);
        }
    } catch (Exception e) {
        //Error handling mechanism
    } finally {
        //Release resources
    }
}

答案 3 :(得分:0)

您可以创建一个具有在其中编写的公共方法的基类,然后从基类继承该类,因此每个子类都将继承它们。

如果拥有超类不是一个选项(受框架或其他一些原因限制),您可以编写一个具有静态方法的实用程序类来实现常用功能。