[an error occurred while processing this directive] IT • archiv :: Print

IT • archiv


[an error occurred while processing this directive]

[an error occurred while processing this directive]

Создание каркаса для Java сервлетов с использованием Reflection. Часть 2

[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive](none)
[an error occurred while processing this directive]([an error occurred while processing this directive] Майкл Цимерман [an error occurred while processing this directive])

[an error occurred while processing this directive](none)

Получите большие функциональные возможности с меньшим количеством кода, используя код Reflection.

Code Tutorial
PDF versionPDF версия
Обзор
В заключительной части этой статьи, разбитой на две части, Майкл Цимерман представляет удобный набор кодовых фрагментов и основных принципов для создания каркаса из Java сервлетов. Основываясь на концепциях, обсужденных в части 1, он раскрывает конкретное представление этого каркаса, чтобы помочь Вам перевести информацию в практический, расширяемый код. В то время как заголовок говорит, что все это применимо только к серверному программированию, большинство понятий может применяться как при разработке программ со стороны клиента, так и программ уровня предприятия. (4700 слов)

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

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

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

Примеры в этой статье будут касаться разработки:

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

Резюме

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

Бизнес объекты

Бизнес объект связан с каждым действием приложения. Например, все объекты, построенные для управления формами в приложении, отвечают за правильность ввода ввода, обработку ввода, связь с интерфейсом доступа к данным, и представление результата пользователю. Подобным образом, все бизнес объекты для страниц с прямыми HTML связми, могут содержать поведение для анализа пути, принятого пользователем, доступа к данным, и вывода результатата.

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

Разработка бизнес объектов и проверка правильности

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

Как только тип действия идентифицирован, можно получить экземпляр бизнес объекта. Прокси сервлет может инициализировать бизнес объект следующим образом:

try{
 businessObject.init( ... );
} catch (ExampleException e){
 exceptionHandler.handleException( e);
}

В части 1 из этой статьи, я продемонстрировал, что Вы могли создавать эти бизнес объекты, ища классы, связанные с названием действия, которое привязано к странице, используя Reflection. Давайте предположем, что я уже создал бизнес объект для этого примера.

Интерфейс BusinessObject определяет init метод. Этот метод берет Hashtable входных параметров, переданные от сервлета. Используя этот Hashtable, бизнес объект может отыскивать данные пришедшие от сервлета без того, чтобы применить для этого объект HttpServletRequest, таким образом привязываясь к платформе развертывания.

Вы можете развивать иерархию бизнес объектов, чтобы создавать большие функциональные возможности с меньшим количеством кода. Поскольку прокси сервлет использует метод forName объекта Class для нахождения бизнес объекта, не требуется никакого алгоритма для отслеживания того, какой бизнес объект следует загрузить. Другими словами, вам нет необходимости писать код, который анализирует название действия и определяет, действительно ли оно ассоциировано с формой или страницей.

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

В моем примере приложения обрабатывает ввод через форму и выводит сообщение на экран пользователя. Я разработал некоторые простые объекты, чтобы представить этот простой каркас. Их названия и действия показываются ниже:

  • DefaultBusinessObject:
    • Проверка правильности ввода пользователя
    • Обработка ввода
    • Вывод результата
  • ReportBusinessObjectextends DefaultBusinessObject
    • Запросы к базе данных
    • Отображение результата пользователю

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

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

Имя поля Тип поля Обязательно?
First Name Text Yes
Last Name Text No
Email Address Email Yes
City Text Yes
State State No
Country Text Yes

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

Это — простой пример, участвующий в демонстрации того, как каркас может управлять бизнес правилами отражающе. Более комплексный пример включил бы проверку правильности групп взаимодействующихся полей, типа группы Сredit Сard с полями даты истечения срока действия и номером карточки. Эти объекты проверки правильности группы могут также вписываться в предложенный каркас, добавляя дополнительные поля к таблицам свойств.

Вы можете использовать различные механизмы, чтобы загружать бизнес правила в каркас. Одна простая схема включает использование статического класса, называемого BusinessRules. Этот класс будет загружать и сохранять все бизнес правила так, чтобы они могли быть связаны с приложением как статические переменные. Ниже приведен пример:

// initialization of the hashtable
public static Hashtable hRegisterUserRules = new Hashtable();

// a lookup value to enter/find the field names
 // — makes the interface cleaner to use.
public static final String sFirstName = "FirstName";

static {
 // create a rule object
 Rule rule = new Rule();

 // set the validation
 Rule.setValidation(Rule.TEXT);

      // indicate whether or not the field is required
      Rule.setMandatory(true);

 // add the rule to the hashtable with the fieldname as the key
      hRegisterUserRules.put(sFirstName, rule);
}

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

Давайте предположим, что другие поля от действия RegisterUser были введены в класс BusinessRules так, как показано выше. Мы можем теперь продолжить обсуждать перенастраиваемый механизм проверки правильности, предложенный для этого каркаса. Эта проверка правильности содержалась бы в BusinessObject, который содержит защищённый метод isValid. Этот метод ответствен за координирование загрузки BusinessRules, ассоциированного с выполняемым действием, и обеспечением того, чтобы поля, поступаемые от пользователя, соответствовали этим правилам.

На этой ступени команда разработчиков должна выбрать, как она будет управлять многостраничными формами. Должно ли быть одиночное действие для каждой страницы или они будут доступны для разных страниц? В примере, описанным здесь, не имеется никакого ограничения на размещении полей на одной форме или на нескольких. Объект данных будет отслеживать любые отсутствующие поля при попытке записать результат в базу данных. Это дает команде разработчиков бизнес объектов большую гибкость при разработке интерфейса пользователя.

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

private Hashtable loadBusinessRules(String sActionRules)
 throws UnexpectedException
{
// Initialize the rules table to null
 Hashtable rulesTable = null;

 try {
  // Load the BusinessRules Class using the static variable
             // containing the fully qualified path value.
  Class c = Class.forName( BusinessRules.BUSINESSRULES);

  // --- Get the hashtable based upon the action name ---//
 / --- (using reflection) --- //
  // The loaded class (BusinessRules) has a field such as
             // RegisterUserRules that we try to load.

  // If the load is successful,
             // we call get(null) to load
 	     // the actual object rather than the value
  // We cast the value as a Hashtable and return it to the user.
  rulesTable = (Hashtable) c.getField(sActionRules).get(null);

 } catch (Exception e) {

  // The hashtable was not found.
  // All actions will have some form of
  // rule interface.

  throw new UnexpectedException(
                 "No business rules exist
 for this Action," + sActionRules);
 }

 return rulesTable;
}

В этом случае, вы загружаете Hashtable целиком в BusinessRules классе. Это позволит Вам использовать методы, содержащиеся в пределах объекта Hashtable, для выполнения проверки правильности.

Ниже представлен пример кода метода isValid, иллюстрирующий, как могла бы быть выполнена проверка правильности:

  // Load the rules table from the business rules object
  // this method may be moved to a utility class later!
  hRulesTable = loadBusinessRules(sActionName);

  // Retrieve the keys from
  // the businessData hashtable passed
  // into the isValid method.
  Enumeration enumKeys = hBusinessData.keys();

  // Initialize the rule object
  Rule rule = null;

  // While there are still keys remaining in the business data
  while (enumKeys.hasMoreElements() ) {
   // Store the key name
   String fieldName = (String) enumKeys.nextElement();

   // Store the user-entered value
   String userValue = (String) hBusinessData.get( fieldName);

   // Retrieve the rule from the rules table
   Rule rule = (Rule) hRulesTable.get(fieldName);

   // the field is in the mandatory hash table
   if ( rule != null) {
                   // the field is mandatory, either because it is required

    // retrieve rules
    String fieldType = rule.getValidation();
    boolean bFieldIsRequired = rule.isMandatory();

                        // trim the extra spaces from the field
    userValue = userValue.trim();

    // check if the field is blank
    boolean fieldNotBlank = (userValue.length() > 0);

    if ( fieldNotBlank) {
     // if not blank, perform field-level validation
     isFieldValid =
                                 validateField( fieldName, fieldType, userValue);
    } else if ( fieldIsRequired) {
     // if the field is blank, it may or may
                               // not be valid, depending on
     // whether or not it is required.

     // If the field is required, it is not valid
     // If the field is not required, it is valid
     isFieldValid = false;
    }

    // If the field is not valid, a missing tag will appear
    if ( !isFieldValid) {
     setError( fieldName, businessData);
    }

   } else { // the field is not contained in the hashtable

    // If this is not a field we care about
    if ( ignoreField( fieldName) )
    {
     // just ignore here
    } else {
     // the value is missing from the table
     // there's a BIG problem!!!

     throw new DataProcessingException(
                                  "missing key value for key "
                                  + fieldName
                                  + " in rules table" );
    }
   }

   // if there's an error, add the error to the businessData

   // The return value is the previous value AND the current
                  // field-level value
   returnValue = returnValue && isFieldValid;

   // reset the field-level validation for the next iteration
   isFieldValid = true;
  }
   return returnValue;
 }

Это большой фрагмент кода, но это полностью независимая от индивидуальных форм проверка на правильность. Давайте быстро это осмыслим. Первоначально, бизнес правила, соответствующие форме загружены, используя ранее описанный метод loadBusinessRules. Имея Hashtable содержащую правила, Вы отыскиваете ключи, соответствующие бизнес информации, которую форма содержит. Вы в цикле переберёте все ключи и найдёте пары название-значение для проверки правильности. Как только название найдено, Вы можете использовать его, чтобы найти соответствующее правило в Hashtable. Если Вы находите правило в Hashtable, Вы можете определить, является ли оно пустым, или требуемым. Если поле — не пустое, то вызывается метод validateField для выполнения проверки правильности.

Метод ValidateField также использует отражение, чтобы загрузить объект проверки правильности, связанный с подтверждаемым полем. Например, поле электронной почты имеет EmailValidation класс. Этот класс был бы ответствен за использование некоторых базисных правил, чтобы определить, похоже ли введенное поле на адрес электронной почты — есть ли символ (@), например. Я не вдаюсь в подробности проверки правильности в этой статье, потому что это довольно очевидно.

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

Обработка ошибок

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

Разработка обработки ошибок

Как описано выше, прокси сервлет вызывает бизнес объекты. Бизнес объекты там инициализируются и начинают выполнять связанные с ними процессы. Запрос на инициализацию бизнес объекта присутствует в пределах try-catch блока, который будет отслеживать ExampleException. Это исключение приведет к вызову программы реакции на исключительную ситуацию, чтобы выполнить необходимые процессы, связанные с исключением.

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

Давайте предположим, что категория неисправности базы данных классифицирована как критическая. DatabaseFailureException расширил бы CriticalException, который будет тогда расширять ExampleException. Таким образом, более определенное поведение могло формироваться универсальным обработчиком ошибок.

Следующие примеры кода показывают развитие от универсальной обработки ошибок до обработки неисправности базы данных. (см. Ресурсы для получения полного исходника)

Public abstract class ExampleExceptionHandler
 {
 public abstract void handleException
          throws ExampleException(ExampleException e);
}

Класс ExampleExceptionHandler — абстрактный класс; он используется как основной класс реакции на особую ситуацию, которую вы можете расширить. Этот класс может содержать функциональные возможности, в зависимости от требований вашего приложения.

public class CriticalExceptionHandler extends ExampleExceptionHandler {
 public void handleException throws ExampleException(CriticalException e) {
  // Send email to the application support team
  sendMail("mcymerman", "error", e.getMessage() );
 }

 public void sendMail(String to, String subject, String message) {
  // normal commands to send email
  ...
 }
}

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

public class DatabaseFailureExceptionHandler extends CriticalExceptionHandler{
  public void handleException(DatabaseFailureException e)
    throws ExampleException
  {
 // send mail to the database support team
 sendMail("dba", "error",
 "fix the database please "+ e.getMessage() );
  }
}

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

Теперь, когда я разработал простой обработчик ошибок каркаса, я объясню, как обработчики исключительных ситуаций будут созданы и вызваны. Как я упомянул ранее, сервис исключений найдёт имя класса определенного исключения, которое было передано, используя метод getName() объекта Class.

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

try {
 ExceptionHandler exceptionHandler =
 loadExceptionHandler( exception);
 ...
} catch ( ClassNotFoundException cnfe){
 // load default exception handler
 ...
 }

ExceptionHandler загружен динамически, основываясь на соглашении об именах объектов. Правда, эта схема ограничивает ваш выбор имён; однако результирующая конструкция очень упрощена. Код в try-catch блоке, показанном выше использует закрытый метод сервиса исключения для загрузки соответствующего кода обработки исключительных ситуаций. Этот метод может быть разработан так как показано ниже:

private ExceptionHandler loadExceptionHandler(
 String sExceptionName, Exception exception)
{
 // Should come from a properties file
      String HANDLER = "Handler";
  String sClassName = null;

 if (sExceptionName == null && exception == null) {
  // load the default exception handler and return
  ...
 }

      if ( sExceptionName == null) {
       // the sExceptionName is passed in from the parent class
  Class classException = exception.getClass();

  sClassName = classException.getName();
 } else {

  sClassName = sExceptionName;
       }

 // The string to load is the name
 // of the exception plus the string
      // identifying it as a handler
      String sExceptionHandler =
  classException.getName()+ HANDLER;

 ExceptionHandler ehExceptionHandler = null;

 try{
 // attempt to load the exception handler using the class.forName
      } catch (ClassNotFoundException exClassNotFound){

   // Get the name of the parent class of the exception
   sExceptionHandler = classException.getSuperClass().getName();

   if ( sExceptionHandler != DEFAULTEXCEPTIONHANDLER ){
     // load the parent class exception handler
           // (this is a recursive method,
           // until the DefaultExceptionHandler is returned)
     loadExceptionHandler( sExceptionHandler, exception);

        }else{
     // load the default error handler
        }
      }

      return ehExceptionHandler;
}

Этот каркас обработки особых ситуаций позволит вам создать вашу обработку исключительных ситуаций в той степени, в какой вам необходимо. Обработчик исключительных ситуаций будет работать посредством иерархии обработчиков исключительных ситуаций и грузить наиболее необходимый обработчик. Если перебрано полное дерево от начала до DefaultExceptionHandler, то будет загружен этот обработчик. Что вы получите, применив этот каркас? Вы теперь будете иметь уверенность, что обработчик исключительной ситуации будет управлять исключительной ситуацией. Вы также получаете возможность по-разному управлять требуемыми ошибками, вроде неисправностей базы данных, или неисправностей вывода результата, через ExceptionHandlers. Обработка требуя грузить различные процедуры обработки исключительных ситуаций через централизованный код, уменьшает усилия при программировании проекта.

Доступ к данным

Статьи относительно каркасов часто изобилуют многочисленными исключениями к каждому правилу, которое они выдвигают. В части 1, я представил простой интерфейс для доступа к данным. Трудно представить каркас, который рассматривает каждый случай — так что я выбрал для показа простой объект, который Вы можете использовать, чтобы регистрировать пользовательские действия в базе данных. Если ваше приложение требует запроса для представления данных, Вы должны использовать другой интерфейс.

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

Разработка доступа к данным

Я выбрал ниже приведённый интерфейс базы данных для выполния функций запроса и регистрации:

public interface DataInterface
{
 // To connect and disconnect
 public void connect() throws DatabaseConnectivityException;
 public void disconnect() throws DatabaseConnectivityException;

 // For logging of user actions and performed transactions.
      public boolean createTransaction() throws DatabaseConnectivityException;
     public boolean beginTransaction() throws DatabaseConnectivityException;
 public boolean commitTransaction() throws DatabaseConnectivityException;
 public boolean rollbackTransaction() throws DatabaseConnectivityException;

 // For the logging and queries.
 public Vector execute(String sqlString)
           throws DatabaseConnectivityException;
}

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

Как вы, вероятно, уже заметили, метод execute возвращает объект Vector. Этот позволяет вам использовать метод execute для выполнения запросов вставки, модификации, и удаления в базе данных по мере необходимости. Почему я выбрал использование Vector вместо JDBC ResultSet? Мне кажется, что код ориентированный на работу с базой данных должен содержаться в классах, самых близких к базе данных, а не в классах, удаленных от SQL. Имеется большое количество накладных расходов, делая эту абстракцию первым кандидатом на оптимизацию, если характеристики вашей системы замедляют обработку больших запросов.

Используя философию, подобную BusinessRules, вы можете использовать объект DatabaseRules, чтобы сохранить SQL, необходимый для выполнения транзакции. Я выбрал Vector, чтобы быть увереным, что SQL выполнен в порядке при гарантии относительной целостности.

// initialize the vector to contain 
// the data rules for the register user action
// инициализация вектора, содержащего правила 
// данных для регистрации действий пользователя
 public static Vector RegisterUserRules = new Vector();

Вот пример SQL запроса, разделённого на части:

public static final String TRANSACTION_STRING =
 "Insert into Transactions values (" +
 TICK + BEGIN + BusinessRules.CONFIRMATION_NUMBER + END
 + TICK + COMMA + SPACE
 + BusinessRules.SYSDATE
 + COMMA + SPACE
 + TICK + BEGIN + BusinessRules.SESSION_ID + END
 + TICK + COMMA + SPACE
 + TICK + BEGIN + BusinessRules.ACTION_NAME + END
 + TICK + COMMA + SPACE
 + TICK + BEGIN + BusinessRules.SEQUENCE_NUMBER + END
 + TICK
 + ")";

SQL запрос добавляется к вектору, как показано выше. Он может быть найден, используя отражение подобным способом как в методе loadBusinessRules, описанному при разработке BusinessObject. Я выбрал простой способ, которым вы можете вставить значения в SQL строку для большей гибкости. Эта схема не требует, чтобы вы вставили все поля в требуемом порядоке; таким образом, вы свободны от строгого интерфейса между бизнес объектом и объектом базы данных.

Загрузив DataRules отражающе, используя название действия, вы можете выполнить подстановку, сопровождаемую выполнением SQL в базе данных. Код для DefaultDataObject довольно очевиден, со следующими методами, выполняющими большую часть режимов работы:

  • ReplaceTokens: Заменяет значения разделителей действительными значениями, полученными в бизнес объекте.
  • BuildSQLString: Анализирует SQL запрос и несколько раз вызывает метод ReplaceTokens.
  • Execute: Выполняет SQL запрос и преобразовывает набор результатов в вектор векторов.

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

Вывод результата

Имеются многочисленные способы управления с уровнем вывода результата — вы можете использовать все что угодно, от XML до инструмента наподобие HTMLKONA. Период разработки и навык команды разработчиков должны быть рассмотрены перед решением этого вопроса. Во многих случаях, проще найти проектировщиков HTML чем разработчиков программного обеспечения. Хорошее решение этого ситуации включает использование шаблонов HTML, которые могут включать JSP.

Графический художник создает шаблоны с метками — заполнителями для переменных элементов данных в конструкции. Таким образом, большинство создаваемых страниц будут статическими, с некоторыми динамическими данными, добавленными во время выполнения. Следующая глава разъясняет эту модель на простом примере.

Разработка вывода результата

Уровень вывода результата тесно взаимодействует с большей частью серверных приложений, представленных на рынке. В случае Netscape Application Server, имеется метод для обработки шаблонов и вывода HTML пользователю.

Если вы полагаете, что из-за близости к интерфейсу пользователя, изменение платформы, вероятно, приведет к конфликту на этом уровне, вы должны попытаться изолировать определенные функциональные возможности от большой части ваших разработчиков. Вы можете выполнить это через интерфейс PresentationServices. При реализации этого интерфейса, вы создадите платформо-зависимый код для связи серверных приложений, Web серверов, и создателей шаблонов. Методы,в этом интерфейсе позволят другим командам разработчиков использовать эти функциональные возможности без того, чтобы полностью изучать платформу развертывания. Имеется базисный интерфейс PresentationServices:

public interface PresentationServices{
 public void initializePresentation(ProxyInterface proxy)
          throws IOException;
 public void output(String text) throws IOException;
 public void finalizePresentation() throws IOException;
}

После реализации, PresentationObject приходит ссылкой на PresentationServices. Используя эту ссылку, PresentationObject может поручать своё поведение платформо-зависимому объекту при поддержании нежесткого соединения между объектом и платформой развертывания. Таким образом, PresentationObject изолирован от кода платформы развертывания, но может тем не менее выполнять режимы работы, заданные бизнес действием.

Взаимодействие между бизнес объектами и объектами вывода результата управляется через PresentationInterface. Этот интерфейс в свою очередь имеет методы, которые позволяют бизнес объектам взаимодействовать с уровнем вывода результата. Объект вывода результата дает доступ к своим методам, но не уступает свои позиции как главнога эксперта по обработке результата. Такое формирование пакета позволяет каждой команде сфокусироваться на их главных задачах разработки и не бояться, что другая команда неправильно использует их объект.

public interface PresentationInterface
{
 public boolean isLastPage();
 public void showNextPage(Hashtable pageData);
 public void showPage(Hashtable pageData);
 public void showConfirmation(Hashtable confData);
 public void showReference( Hashtable referenceData);
 public void showErrorPage(String errorMessage);
 }

Для описания моего примера, имеются две страницы, которые я разработал для этой статьи. Они могут быть найдены в Ресурсах.

  • Страница регистрации: Собирает информацию о пользователе для использования её в отчёте.
  • Страница отчёта: Вывод отчёта используя информацию зарегистрированных пользователей, их города и страны.

PresentationRules загружаются также, как и BusinessRules и DataRules, с названием действия, вырабатывая тем самым ключ размещения правил. В этом случае, вектор содержит все страницы для данного действия. В случае многократного действия страницы, в векторе будет несколько страниц.

public static Vector RegisterUserRules = new Vector();

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

RegisterUserRules.addElement(LOCATION + "RegisterUser.html");

Объект PresentationServices не содержит никакого процедурного кода. Он управляется бизнес объектом, чтобы гарантировать, что вывод результата взаимодействует с бизнес логикой. Имея загруженный объект PresentationRules, PresentationObject может взаимодействовать с бизнес объектом, поскольку они выполняют их функциональные возможности.

Например, действие RegisterUser будет иметь единственную страницу асоциированую с ним. Когда BusinessObject взаимодействует с PresentationObject, будет определено, что имеется только одна страница, и вызывана транзакция, которая укажет, что действие было выполнено. Если действие содержит несколько страниц, бизнес объект будет проходить по ним, пока не будет достигнута последняя страница. Это позволяет бизнес объекту положиться на объект вывода результата в определении числа страниц, уменьшая зависимость.

Дополнительные страницы могут быть просто добавлены к выводу результата, вставляя дополнительные ссылки в Vector. Поскольку бизнес объекты не зависят от определенных страниц, на которых находятся поля, можно делать изменения вывода результата без изменений на бизнес уровне.

Заключение

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

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

Если вы тратите время на проектирование и имеете квалифицированную команду разработчиков, то это — хорошая методология. Она требует дисциплинированную и методичную команду чтобы преимущества были полностью получены. Кроме того, основные системы, для которых пишется код, должны иметь некоторую стабильность во время проектирования приложения.

Чтобы просмотреть полностью рабочую версию каркаса, описанного в этой статье, обратитесь к Ресурсам.

Об авторе

Майкл Цимерман — консультант, специализирующийся в решениях, основанные на Java / Internet. Майкл предоставляет Java/Internet архитектуры и разработки для компаний, входящих в состав Fortune 500. Полный код для этой статьи можете найти по адресу http://www.itpath.com/articles.

Ресурсы

Reprinted with permission from the February 2000 edition of JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://www.javaworld.com/ javaworld/jw-02-2000/jw-02-servlets2.html

[an error occurred while processing this directive]
[an error occurred while processing this directive] Перевод на русский © Павел Цуканов, 2000
< Вернуться на caйт :: Copyright © 1999 — 2010, IT • archiv.