存储库的继承或委派

时间:2019-02-09 19:13:06

标签: java inheritance design-patterns delegates

说我有一个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中。

0 个答案:

没有答案