ОО противМногослойный;балансирование между “чистотой OO” и доведением дела до конца

StackOverflow https://stackoverflow.com/questions/153394

  •  03-07-2019
  •  | 
  •  

Вопрос

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

Итак, как работать с многоуровневой архитектурой Serlvet / EJB / DataContainer:

  • Сервлеты получают запросы и вызывают "бизнес-уровень" (например,сеансовые EJBS)
  • Бизнес-уровень находит контейнеры данных в базе данных и манипулирует ими для реализации бизнес-логики
  • DataContainers не содержат реального кода, просто get / set, соответствующего базе данных.

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

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

Даже если мы были пытаюсь быть более "ОО" (например,помещая некоторые из этих методов в DataConatiners), некоторые из этих операций работают с несколькими наборами данных.

Как сделать так, чтобы ваш бизнес-уровень не становился запутанно процедурным, но при этом не загрязнял ваши DataContainers бизнес-логикой?

Пример

class UserServlet {
  handleRequest() {
    String id = request.get("id");
    String name = request.get("name");
    if (UserBizLayer.updateUserName(id,name))
      response.setStatus(OK);
    else
      response.setStatus(BAD_REQUEST);
  }
}

class UseBizLayer {
    updateUserName(String id, String name) {
        long key = toLong(id);
        user = userDAO.find(key);
        if user == null
            return false;
        if (!validateUserName(name))
            return false;
        user.setName(name);
        userDAO.update(user);
        return true;
    }

    validateUserName(String name) {
        // do some validations and return
    }
}

class User {
    long key;
    String name;
    String email;

    // imagine getters/setters here
}
  • Мы не хотим validateUserName на пользователя, поскольку он оперирует только именем;Я думаю, это можно было бы перенести в другой класс, но тогда у нас есть другой класс процедурного типа "uti"
  • Нам не нужны методы сохранения для Пользователя, поскольку есть смысл отделить структуры данных от их стратегии сохранения
  • Нам не нужна бизнес-логика в нашем сервлете, поскольку нам может потребоваться повторно использовать эту логику в другом месте
  • Мы не хотим, чтобы наша бизнес-логика была в нашем пользователе, поскольку это слишком сильно привязывает к классу User, затрудняя повторное использование бизнес-логики и связывая пользователя с его стратегией сохранения

Я понимаю, что этот пример не так уж плох, но представьте себе 10 DataContainers и 20 объектов BizLayer с несколькими методами каждый.Представьте, что некоторые из этих операций не "центрированы" на конкретном контейнере данных.

Как нам уберечь это от процедурной неразберихи?

Это было полезно?

Решение

Итак, я изложу свои мысли по этому поводу в нескольких основных пунктах:

  1. Похоже, что в системе Java EE в какой-то момент вам приходится иметь дело с сантехникой Java EE, сантехника не всегда выигрывает от концепций OO, но, безусловно, может, если проявить немного творчества и поработать.Например, вы могли бы воспользоваться такими преимуществами, как AbstractFactory и т.д., чтобы максимально упростить эту инфраструктуру.
  2. Многое из того, что вы изучаете, обсуждается в превосходной книге Эрика Эванса под названием Domain Driven Design.Я настоятельно рекомендую вам взглянуть на это так, как он рассматривает проблему выражения знаний о предметной области и работы с технической инфраструктурой для ее поддержки.
  3. Прочитав и изучив некоторые из DDD, я бы инкапсулировал свою техническую инфраструктуру в репозитории.Все репозитории будут написаны так, чтобы использовать стратегию сохранения, основанную на EJBS вашего сеанса.Вы бы написали реализацию по умолчанию, для которой известно, как взаимодействовать с вашими EJBS сеанса.Чтобы это сработало, вам нужно было бы добавить немного условностей и указать это соглашение / контракт в ваших интерфейсах.Репозитории выполняют весь CRUD и должны делать больше, чем это, только в случае крайней необходимости.Если бы вы сказали: "Мои DAO - это мои хранилища", то я бы согласился.
  4. Итак, продолжим с этим.Вам нужно что-то, что инкапсулировало бы единицу работы, выраженную в UseBizLayer.На этом уровне, я думаю, природа этого такова, что вы застряли на написании кода, который весь будет сценарием транзакции.Вы создаете разделение ответственности и государства.Обычно я видел, как это делается в системах Java EE в качестве архитектуры по умолчанию.Но это не объектно-ориентированный метод.Я бы попытался изучить модель и посмотреть, смогу ли я, по крайней мере, попытаться унифицировать некоторые модели поведения, которые записаны в бизнес-классах.
  5. Другой подход, который я использовал ранее, заключается в том, чтобы избавиться от классов BizLayer, а затем перенаправить вызовы из Домена в фактические репозитории / DAO, выполняющие операцию.Однако это может потребовать определенных инвестиций в создание инфраструктуры.Но вы могли бы многое сделать с таким фреймворком, как Spring, и используя некоторые концепции AOP, чтобы заставить это работать хорошо и сократить объем необходимой пользовательской инфраструктуры.

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

поскольку вы реализуете классы и объекты, ваше решение будет OO независимо от того, как вы распределяете вещи - оно просто может быть не очень хорошо структурировано в зависимости от вашей ситуации / потребностей!;-)

что касается вашего конкретного вопроса, в некоторых случаях имело бы смысл, чтобы validateUserName принадлежало классу User, поскольку каждый пользователь хотел бы иметь допустимое имя.Или у вас может быть служебный класс проверки, предполагающий, что у других объектов есть имена, которые используют те же правила проверки.То же самое относится и к электронной почте.Вы могли бы разделить их на классы NameValidator и EmailValidator, что было бы прекрасным решением, если они будут часто использоваться.Вы также могли бы по-прежнему предоставлять функцию validateUserName для объекта User, который только что вызвал метод служебного класса.Все это - действительные решения.

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

В этом случае я бы создал классы NameValidator и EmailValidator, потому что представляется вероятным, что в будущем у других объектов будут имена и адреса электронной почты, но я бы предоставил функции validateName и validateEmailAddress в классе User, потому что это создает более удобный интерфейс для использования бизнес-объектами.

остальные пункты "мы-не-хотим" верны;они необходимы не только для правильного наложения слоев, но и для чистого OO-дизайна.

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

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

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

Хранит ли клиент свои данные?Нет, это делает сотрудник.Какую роль играет сотрудник, когда он хранит данные клиента?Хранитель клиентских записей.

Подтверждает ли форма, что она была заполнена правильно?Нет, это делает сотрудник.Какую роль играет сотрудник, когда он это делает?Обработчик форм.

Кто предоставляет клиенту виджет?Сотрудник, выступающий в качестве распространителя виджетов

И так далее...

Чтобы внедрить это в реализацию Java EE...

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

У меня самого были такие же мысли.

В традиционном MVC самое важное - отделить представление от частей модели и контроллера.Кажется хорошей идеей разделить контроллер и модель просто потому, что в итоге вы можете получить раздутые объекты модели:


public class UserModel extends DatabaseModel implements XMLable, CRUDdy, Serializable, Fooable, Barloney, Baznatchy, Wibbling, Validating {
  // member fields
  // getters and setters
  // 100 interface methods
}

Принимая во внимание, что у вас могут быть отдельные контроллеры (или целые шаблоны) для многих интерфейсов, описанных выше, и да, это скорее процедурно по своей природе, но я думаю, что именно так все работает в наши дни.В качестве альтернативы вы можете понять, что некоторые интерфейсы выполняют одно и то же (CRUDdy - хранение и извлечение базы данных, Serializable - то же самое для двоичного формата, XMLable, то же самое для XML), поэтому вам следует создать единую систему для обработки этого, причем каждый потенциальный серверный сервер является отдельной реализацией, которую обрабатывает система.Боже, это действительно плохо написано.

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

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

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

Контейнер данных очень похож на шаблон Объектов передачи данных (DTO).

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

(+ 1 за вопрос, + 1 за ответ о том, когда вы узнаете, что правильно распределили слои)

Грубо говоря, раздел "Мы не хотим" должен быть удален.Либо вы хотите, чтобы все было правильно, либо хотите, чтобы все оставалось как есть.В этом нет никакого смысла нет создание класса, когда он вам нужен."У нас их предостаточно" - плохое оправдание.

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

Кроме того, разве шаблон фасада не является чем-то, что решает проблему существования множества классов BizRules, скрытых за одним хорошо организованным и чистым интерфейсом?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top