Как создать прокси для методов?

1

Проблема

У меня есть класс обслуживания, который содержит несколько методов, как показано ниже. Специфика того, что делает этот класс, описана в конце этого сообщения.

public class Service{
    public Foo m_a( /* args_a */ ){ ... }
    public Bar m_b( /* args_b */ ){ ... }
    // and so on
}

Все общедоступные методы в этой службе имеют одну общую черту: они вызывают метод init() прежде чем делать что-либо еще. Итак, моя услуга выглядит примерно так:

public class Service{
    private int attr;
    private void init(int val){ 
        this.attr = val; 
    } 

    public Foo m_a(int val_a, ...){
        init(val_a); 
        // time for business A
    }
    public Bar m_b(int val_b, ...){
        init(val_b);
        // time for business B
    }
    // and so on
}

Поскольку другие методы могут быть добавлены в Service в будущем, я думаю, что дизайн выше довольно беден. Парень, который реализует новый метод для этого класса, может сначала не следовать правилу вызова init(val_x), и это может привести к ошибкам.

Проект решения

Чтобы гарантировать, что init() всегда вызывается, я могу создать некоторый прокси-метод, который выполняет работу систематически:

public class Service{

    // Init method
    private int attr;
    private void init(int val){ 
        this.attr = val; 
    }

    /* 
     * The only public method in the service is a proxy, 
     * that always calls init() before calling the actual requested method
     */
    public Object invoke(String method, int val, Map<String,Object> args){  

        init(val);

        switch(method){
            case "m_a":
                return m_a(args.get("name"),...);
            case "m_b":
                return m_b(args.get("myArg"),...);
            default: // launch some exception
        }
    }

    private Foo m_a( /* args_a */ ){ ... }
    private Bar m_b( /* args_b */ ){ ... }
    // and so on
}

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

Однако я вижу большой недостаток в этом решении: удобство использования для пользователя!

  • Нужно поместить все свои аргументы в объект Map перед вызовом метода invoke, вместо того, чтобы вводить аргументы непосредственно в подписи метода
  • Возвращаемым типом invoke является Object, который чрезвычайно неоднозначен и требует от пользователя применения кастинга при каждом вызове invoke

У вас, ребята, какая-нибудь умная идея о том, как удалить эти проблемы, и все еще держите эту идею прокси?

Работа класса Service

Фактически я занимаюсь внедрением REST API. Целью этого API является доступ к конкретным ресурсам, хранящимся в базе данных. Например:

GET http://fortheking.com/topic/3

Эти HTTP-запросы будут связаны с методом службы, который извлекает конкретную тему, учитывая ее идентификатор.

На практике "тема" - это имя ресурса, к которому необходимо получить доступ. Это точка входа для каждого метода моего класса Service, и поэтому существует некоторый общий код для применения до фактического запуска предполагаемой логики метода. Я поставил такой код в init().

  • 0
    Не могли бы вы немного конкретизировать, что делают ваш класс и метод init ? Лично мне вообще не нравится этот дизайн прокси. Я предполагаю, что в дизайне вашего класса есть еще одна «проблема», которая вызывает необходимость init в ваших сообщениях.
  • 0
    Я бы предпочел увидеть отражение, чем это. Почему бы просто не использовать АОП, если это настоящая проблема?
Показать ещё 1 комментарий
Теги:
design-patterns

1 ответ

1

По сути, вы хотите использовать Java-прокси с помощью ApplicationHandler. Этот блог немного устарел, но объясняет основные понятия: http://www.ibm.com/developerworks/library/j-jtp08305/ - раздел "Динамические прокси-серверы", возможно, наиболее важен для вас. Идея заключается в том, что вы вернете экземпляр "Service" для ваших вызывающих абонентов, который действительно является прокси-объектом. В этом прокси InvocationHandler вы можете вызвать метод init(), а затем выполнить основной метод. Если у вас есть существующие методы, которые также вызывают init(), вам нужно либо убедиться, что init() является idempotent, либо изменить код, чтобы не вызывать его дважды.

Одно из ограничений этого подхода заключается в том, что вы можете создавать прокси для интерфейса, а не для класса. (Если это ограничение не было отменено в какой-то новой версии Java, на которую я еще не смотрел.) Я признаю, что ваш примерный код выше упрощен, но вы не показываете никаких интерфейсов, так что это может быть что-то, что вам нужно представить первый. Самое главное, что вызывающие абоненты вашей службы должны напрямую использовать интерфейс, а не класс сервиса.

  • 1
    прокси для классов не поддерживается по умолчанию в JDK. Однако есть сторонние библиотеки, которые предоставляют такую поддержку. CGLib является одним из наиболее используемых программ. Вот блог, объясняющий, как прокси-класс с помощью cgLib brixomatic.wordpress.com/2012/12/22/dynamic-proxies-for-classes
  • 0
    @dkatzel - спасибо за это. Я не знал, что это возможно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню