Как избежать повторения сложного кода обработки исключений в классе-оболочке?

У меня есть этот класс, который обертывает объект:

public class MyWrapper implements MyInterface {

    private MyInterface wrappedObj;

    public MyWrapper(MyInterface obj) {
        this.wrappedObj = obj;
    }

    @Override
    public String ping(String s) {
        return wrappedObj.ping(s);
    }

    @Override
    public String doSomething(int i, String s) {
        return wrappedObj.doSomething(i, s);
    }

// many more methods ...
}

Теперь я хочу добавить сложную обработку исключений вокруг вызова wrapObj.

Он одинаков для всех методов.

Как избежать многократного повторения одного и того же кода обработки исключений?


person Miguel Pardal    schedule 17.05.2017    source источник
comment
Думаю, нам нужно посмотреть, что за комплекс влечет за собой. Обычно я просто помещаю дублированные части в общий метод и вызываю его там, где это необходимо.   -  person markspace    schedule 17.05.2017
comment
Определить private void handle(MyException exception) и вызывать его из каждого блока catch?   -  person VGR    schedule 17.05.2017
comment
Использовать лямбды? то есть Object doWithExceptionHandling(Callable callable)   -  person Taylor    schedule 17.05.2017


Ответы (2)


Если ваша обработка исключений полностью универсальна, вы можете реализовать оболочку как InvocationHandler:

public class ExceptionHandler implements java.lang.reflect.InvocationHandler {
    public ExceptionHandler(Object impl) {
        impl_ = impl;
    }

    @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            return method.invoke(impl_, args);
        }
        catch (Exception e) {
            // do exception handling magic and return something useful
            return ...;
        }
    }

    private Object impl_;
}

а затем оберните его вокруг экземпляра следующим образом:

MyInterface instance = ...
MyInterface wrapper = (MyInterface)java.lang.reflect.Proxy.newProxyInstance(
   instance.getClass().getClassLoader(), 
   new Class[] { MyInterface.class }, 
   new ExceptionHandler(instance));

wrapper.ping("hello");
person wero    schedule 17.05.2017

Если вы хотите избежать затрат на отражение, просто используйте функцию маршрутизатора.

@Override
public String ping(String s) {
    return (String) call("ping");
}

private Object call(String func) {
    try {
      switch(func) {
        case "ping": return wrappedObj.ping(s);
        // ... rest of functions ... //
      }
    } catch(Exception e) {
      log(e);
    }
}

Компилятор может просто перейти к функции, не подтягивая спецификации или обработчики объекта. (Достаточно умный компилятор может даже просто скомпилировать это в код выполнения, идентичный вашему текущему коду, особенно если вы можете сократить приведение, всегда возвращая объект одного и того же типа)

Если вы не заботитесь о потоке и просто хотите обработчик исключений по умолчанию...

Для всей среды выполнения Java вызовите Thread.setDefaultUncaughtExceptionHandler
Для ThreadGroup переопределите ThreadGroup.uncaughtException
Для одного потока вызовите Thread.setUncaughtExceptionHandler

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

person Tezra    schedule 17.05.2017