try-catch-finally против абстрактных методов - PullRequest
3 голосов
/ 07 июля 2011

в нашей системе у нас есть абстрактный класс, назовем его BasicAction , который содержит несколько абстрактных методов. Самый важный из них называется execute . Он обрабатывает запросы со страниц JSP. Основной обработчик работает так:

// Sample 1: 
String actionName;// The name of action to use
Map<String, BasicAction> mapping;//The mapping of names and actual handlers

BasicAction action = mapping.get(actionName);

try {
  action.execute(request);//Handle the http request from the JSP page
} catch (Exception ex) {
  // Handle here any exceptions
}

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

// Sample 1: 
public class ConcreteAction extends BasicAction {
  @Override
  public void execute(HttpServletRequest request) {
    // The managers provide the middle layer between 
    // web presentation and database
    TrafficManager trafficManager = null;
    CargoManager cargoManager = null;
    try {
      trafficManager = new TrafficManager(); 
      cargoManager = new CargoManager();
      // Perform here all the logic required using managers
    } catch (Exception ex) {
       // handle the exception or rethrow it
    } finally {
      // Should remove all managers clearly and release the connection
      removeManager(trafficManager);
      removeManager(cargoManager);
    }
  }
}

Кажется немного раздражающим писать такие блоки в каждом моем обработчике. Кажется, что здесь мы имитируем точки входа / выхода для каждого обработчика не так, как предполагалось. Я думаю, что нам нужно определить в BasicAction еще два абстрактных метода, называемых createManagers и disposeManagers . Тогда основной обработчик будет выглядеть так:

// Sample 2: 
String actionName;// The name of action to use
Map<String, BasicAction> mapping;//The mapping of names and actual handlers

BasicAction action = mapping.get(actionName);

try {
  action.createManagers(); // The enter point
  action.execute(request);//Handle the http request from the JSP page
} catch (Exception ex) {
  // Handle here any exceptions
} finally {
  action.disposeManagers(); // The exit point
}

После этого каждый обработчик производного действия может быть определен так:

// Sample 2: 
public class ConcreteAction extends BasicAction {
  private TrafficManager trafficManager = null;
  private CargoManager cargoManager = null;

  @Override
  public void createManagers() {
    trafficManager = new TrafficManager();
    cargoManager = new CargoManager();
  }

  @Override
  public void disposeManagers() {
    removeManager(trafficManager);
    removeManager(cargoManager);
  }

  @Override
  public void execute(HttpServletRequest request) {
    // Perform here all the logic required using managers    
  }
}

Какой подход лучше использовать - с try-catch-finally в каждом обработчике или со стандартными точками входа / выхода.

Ответы [ 5 ]

5 голосов
/ 07 июля 2011

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

1 голос
/ 07 июля 2011

Пусть createManagers () возвращает список менеджеров. Вызывающий класс может затем вызывать removeManager () для каждого, что делает ненужным disposeManager ().

Кроме того, вы соединяете BasicAction и ConcreteAction с помощью наследования. Это не обязательно. Вы можете соединить их по составу вместо этого. Если ConcreteAction реализует интерфейс IBasicAction, отдельный класс ActionRunner может вызывать createManagers () и execute () для действия. Вы можете передать экземпляр ConcreteAction в ActionRunner.

0 голосов
/ 07 июля 2011

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

Я думаю, что я 'Я бы сделал еще один шаг вперед.Во-первых, метод execute требует, чтобы эти два менеджера выполняли работу, поэтому я бы определил его как

public void execute(HttpServletRequest req, TrafficManager t, CargoManager c);

Теперь давайте предположим, что большинство ваших действий используют одинаковые реализации менеджера.Мы определяем эти методы в суперклассе (но не делаем их окончательными) следующим образом:

public TrafficManager createTrafficManager() { return new TrafficManager(); }
public CargoManager createCargoManager() { return new CargoManager(); }

Так что теперь суперкласс может сам создавать эти экземпляры, вызывая метод и передавая их в execute.И если подклассу нужна реализация, отличная от стандартной, он может переопределить метод по своему усмотрению.

Глядя на очистку - мы могли бы применить аналогичный подход к описанному выше и определить абстрактные реализации.Однако, если менеджерам необходимо очистить , то, вероятно, они реализуют метод close() или аналогичный.В этом случае мы можем просто вызвать это - что гарантирует, что они будут утилизированы должным образом, независимо от реализаций подклассов, и для try и finally невозможно выйти из синхронизации.

Или вы можете просто позвонить removeManager на каждого, если этого требует ваша логика.(Это может быть улучшено и дальше, но это зависит от семантики этого метода и от того, где он «живет».)

В целом тогда основной код BasicAction выглядит как

BasicAction action = mapping.get(actionName);

// (It's a shame that these need the initial assignment to null due to being
//  referenced in the finally block - it's pretty ugly)
TrafficManager tMan = null;
CargoManager cMan = null;
try {
  tMan = createTrafficManager();
  cMan = createCargoManager();
  action.execute(request, tMan, cMan);//Handle the http request from the JSP page
} catch (Exception ex) {
  // Handle here any exceptions
} finally {
  if (tMan != null) {
    removeManager(tMan); // Is this necessary, did it get registered somewhere after creation?
    tMan.close(); // If they're closeable
  }
  if (cMan != null) { 
    // Of course this block could be a tiny method to further remove duplication,
    // so long as you have a common superinterface for both *Manager classes
    removeManager(cMan);
    cMan.close();
  }
}
0 голосов
/ 07 июля 2011

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

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

0 голосов
/ 07 июля 2011

Перейти абстрактно (вариант 2).

Общий код, особенно общий поток обработки, должен быть абстрагирован. Это освобождает подклассы для реализации отличий и означает, что можно тестировать абстрактный код изолированно, например, с помощью макета / теста.

Обратите внимание, что вы можете зайти слишком далеко, так что здравый смысл применим, но всегда следите за точками абстракции.

...