Наследование или делегирование для хранилищ - PullRequest
0 голосов
/ 09 февраля 2019

Скажем, у меня есть интерфейс Repository (объект для доступа к данным).Я создал простой для этого вопроса.

interface UserRepository {
   void insert(final User user);
   void update(final User user);
   List<User> findAll();
}

Теперь, поскольку я управляю различными типами СУБД, этот репозиторий реализован следующим образом:

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 (обратите внимание, я не получаю подмножество свойств для конкретной СУБД, а вместо этого использую общие):

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...