Шаблон декоратора с постоянно меняющимися интерфейсами

У меня есть вариант использования, когда у меня есть интерфейс базы данных, предоставленный внешним поставщиком, скажем, он выглядит следующим образом:

interface Database{

   public Value get(Key key);

   public void put(Key key, Value value)

}

Поставщик предоставляет несколько реализаций этого интерфейса, например. Актуалдатабасеимпл, Мокдатабасеимпл. Мои потребители хотят использовать интерфейс базы данных, но перед вызовом некоторых API они хотят выполнить некоторую дополнительную работу, например, вызвать ограничитель скорости на стороне клиента перед выполнением вызова. Таким образом, вместо того, чтобы каждый потребитель выполнял дополнительную работу по проверке лимита rateLimiter, я подумал о создании декорированного класса, который будет абстрагировать часть ограничения скорости, и потребители смогут взаимодействовать с БД, не зная логики RateLimiter. например

class RateLimitedDatabase implements Database{

    private Database db;
    public RateLimitedDatabase(Database db) {this.db = db;}

    public Value get(Key key) { 
          Ratelimiter.waitOrNoop();
          return db.get(key);
    }

    public void put(Key key, Value value) {
         Ratelimiter.waitOrNoop();
         return db.put(key, value);
    }
} 

Это прекрасно работает до тех пор, пока интерфейс базы данных не вводит новые методы. Но как только они начинают добавлять API, которые мне на самом деле не нужны, например. Начинают возникать проблемы с удалением/получениемDBInfo/deleteDB и т. д.

Всякий раз, когда выпускается новая версия БД с более новыми методами, моя сборка для RateLimitedDatabase будет ломаться. Один из вариантов — реализовать новые методы в декорированном классе при расследовании основной причины сбоя сборки, но это просто дополнительная проблема для разработчиков. Есть ли другой способ справиться с такими случаями, поскольку это кажется распространенной проблемой при использовании шаблона Decorator с постоянно меняющимся/расширяемым интерфейсом?

ПРИМЕЧАНИЕ. Я также могу подумать о создании решения на основе отражения, но это кажется излишним/чрезмерным для этой конкретной проблемы.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
39
2

Ответы 2

Если это возможно (вам нужно изменить весь клиентский код), вы можете извлечь «зеркало» интерфейса vendor.Database и вызвать его, например. mirror.Database; и скопируйте из интерфейса vendor.Database в интерфейс mirror.Database только нужные вам методы (с теми же сигнатурами).

Отредактируйте код клиента, чтобы использовать интерфейс mirror.Database, и позвольте RateLimitedDatabase реализовать этот mirror.Database интерфейс. Поскольку все сигнатуры методов одинаковы, переключение клиентского кода на зеркальный интерфейс должно быть безболезненным. RateLimitedDatabase, конечно же, делегирует vendor.Database реализацию.

(Я думаю, что то, что я описал, более или менее является шаблоном моста (используя интерфейс для «защиты» от базовых изменений), https://en.wikipedia.org/wiki/Bridge_pattern)

В аспектно-ориентированном программировании есть решение этой проблемы. Большинство фреймворков генерируют динамический прокси для вашего интерфейса, чтобы он всегда был синхронизирован.

Другие вопросы по теме