如何避免 class 的所有方法通用的重复代码?
How to avoid duplicate codes that is common for all methods of a class?
在我们的应用程序中,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 工具。
您可以创建一个基础 class,其中写有常用方法,然后从基础 class 继承 class,这样每个子 class 都将继承它们。
如果有一个超级 class 不是一个选项(受框架或其他原因限制),您可以编写一个实用程序 class 具有用于通用功能的静态方法。
为什么不传入一个知道如何将 ResultSet
转换为您期望的元素列表的 mapper
。例如:
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
方法将保持不变,因此您只需要它的 1 个实现。
如果您在项目中使用 Spring,您可以使用它的 ResultSetExtractor
或 RowMapper
类 来做类似的事情。
因为 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
的声明式事务
这使用了 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
s(在上面的代码中,你可以看到我被迫将 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
}
}
在我们的应用程序中,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 工具。
您可以创建一个基础 class,其中写有常用方法,然后从基础 class 继承 class,这样每个子 class 都将继承它们。
如果有一个超级 class 不是一个选项(受框架或其他原因限制),您可以编写一个实用程序 class 具有用于通用功能的静态方法。
为什么不传入一个知道如何将 ResultSet
转换为您期望的元素列表的 mapper
。例如:
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
方法将保持不变,因此您只需要它的 1 个实现。
如果您在项目中使用 Spring,您可以使用它的 ResultSetExtractor
或 RowMapper
类 来做类似的事情。
因为 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
这使用了 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
s(在上面的代码中,你可以看到我被迫将 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
}
}