说我有一个Repository(一种用于访问数据的对象)接口。 我为这个问题做了一个简单的例子。
interface UserRepository {
void insert(final User user);
void update(final User user);
List<User> findAll();
}
现在,由于我管理的是不同类型的DBMS,因此该存储库是通过以下方式实现的:
class MySqlUserRepository implements UserRepository { ... }
class OracleUserRepository implements UserRepository { ... }
class SqlServerUserRepository implements UserRepository { ... }
由于大多数SQL语句都可以在这些实现之间共享,所以我要做的第一件事是添加一个Abstract基类,该基类提供所有基本实现。
abstract class AbstractUserRepository implements UserRepository {
// Provide access to statements
private final Configuration configuration;
// Provides access to JDBC operations
private final NamedParameterJdbcTemplate jdbcTemplate;
AbstractUserRepository(
final Configuration configuration,
final NamedParameterJdbcTemplate jdbcTemplate) {
this.configuration = configuration;
this.jdbcTemplate = jdbcTemplate;
}
... Implementations
}
所以现在所有的实现extends AbstractUserRepository
。
但是,我必须重写OracleUserRepository
上的方法以使用其他语句...
class OracleUserRepository extends AbstractUserRepository {
private final Configuration configuration;
private final NamedParameterJdbcTemplate jdbcTemplate;
OracleUserRepository(
final Configuration configuration,
final NamedParameterJdbcTemplate jdbcTemplate) {
super(configuration, jdbcTemplate);
this.configuration = configuration.subset("user.sql.oracle");
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void update(final User user) {
// Use Configuration and JdbcTemplate
}
...
}
...和SqlServerUserRepository
上的几种方法,显然都使用相同的方法。
现在,您可以看到我们已经在复制类属性,也许方法基本相同:
AbstractUserRepository#update
:
void update(final User user) {
final var parameters =
new MapSqlParameterSource()
.addValue("field1", user.field1)
.addValue("field2", user.field2);
jdbcTemplate.update(configuration.getString("user.sql.update"), parameters);
}
OracleUserRepository#update
:
void update(final User user) {
final var parameters =
new MapSqlParameterSource()
.addValue("field1", user.field1)
.addValue("field2", user.field2);
// Note the subtle difference. We got a "subset" (user.sql.oracle) in the constructor
jdbcTemplate.update(configuration.getString("update"), parameters);
}
这对我来说不合适,已经有太多重复了。你也不觉得吗?
另一种方法是使用委托。这意味着要拥有一个分离的对象,该对象不继承UserRepository
接口,并且具有的方法也可以接受SQL语句,而不是仅接受对象。
class UserRepositoryDelegate {
private final NamedParameterJdbcTemplate jdbcTemplate,
UserRepositoryDelegate(final NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void update(
final String statement,
final User user) {
final var parameters =
new MapSqlParameterSource()
.addValue("field1", user.field1)
.addValue("field2", user.field2);
jdbcTemplate.update(statement, parameters);
}
...
}
该对象将被注入其他存储库中
class OracleUserRepository implements UserRepository {
private final Configuration configuration;
private final UserRepositoryDelegate delegate;
OracleUserRepository(
final Configuration configuration,
final UserRepositoryDelegate delegate) {
this.configuration = configuration.subset("user.sql.oracle");
this.delegate = delegate;
}
@Override
public void update(final User user) {
delegate.update(configuration.get("update"), user);
}
...
}
AbstractUserRepository
可能会变成具体的GenericUserRepository
(注意,我没有获得特定DBMS的属性子集,而是使用通用属性):
class GenericUserRepository implements UserRepository {
private final Configuration configuration;
private final UserRepositoryDelegate delegate;
GenericUserRepository(
final Configuration configuration,
final UserRepositoryDelegate delegate) {
this.configuration = configuration;
this.delegate = delegate;
}
@Override
public void update(final User user) {
delegate.update(configuration.get("user.sql.update"), user);
}
...
}
GenericUserRepository
也可以被其他实现使用:
class MySqlUserRepository implements UserRepository {
private final GenericUserRepository genericUserRepository;
MySqlUserRepository(final GenericUserRepository genericUserRepository) {
this.genericUserRepository = genericUserRepository;
}
@Override
public void update(final User user) {
genericUserRepository.update(user);
}
...
}
对于这种特殊情况,委托方法是否是更好的策略?
您会以其他方式解决此“问题”吗?
编辑:为了让您了解更多,在启动时选择了唯一且正确的存储库实现,并将其注入到一个具体的UserService
中。