代码之家  ›  专栏  ›  技术社区  ›  LppEdd

存储库的继承或委派

  •  1
  • LppEdd  · 技术社区  · 7 年前

    假设我有一个存储库(一种用于访问数据的对象)接口。 我为这个问题编了一个简单的答案。

    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 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 .

    0 回复  |  直到 7 年前
    推荐文章