Помощь в кажущемся противоречии двух понятий - упор и н-ярус развития - PullRequest
10 голосов
/ 19 января 2011

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

Пожалуйста, скажите мне, где я ошибаюсь при использовании n-уровневой архитектуры. Скажем, я хочу работать с группой людей. Я создаю класс Person на уровне сущностей с FN, LN, BDay и т. Д. Я буду использовать этот класс Person для представления одного человека на всех моих уровнях - UI, Business и DataAccess. Но я, как правило, не могу поместить какие-либо полезные методы в «Person», потому что тогда я буду пересекать границы уровня.

Таким образом, я в итоге создаю UiPersonClass, BusinessPersonClass и DataAccessPersonClass, каждый из которых имеет член класса Person. Затем я заканчиваю тем, что создаю конструктор для каждого класса уровня, который принимает параметр класса «Person» (поступающий из других слоев) и устанавливает его в this.Person . И я добавляю смежный слой PersonClass с this.Person в качестве параметра. и т.д.

Это кажется действительно неправильным, но как еще вы должны это сделать?

Если бы я использовал только один слой, я мог бы иметь класс «Person» с методами, которые заполняют элементы управления пользовательского интерфейса, обрабатывают информацию, сохраняют и извлекают данные из базы данных, все в одном месте, все в одном «объекте» .

Ответы [ 8 ]

6 голосов
/ 25 января 2011

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

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

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

Тогда приведенный вами пример не так прост.

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

Обычно у меня есть в основном однозначное соответствие между данными в базе данных и объектами, которые создаются в DataLayer и передаются туда и обратно в BusinessLayer .

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

Эти объектыкак правило, очень разные.

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

Когда все начинает усложняться, оказывается, что так делать гораздо проще.Особенно, когда в приложение вносятся изменения

5 голосов
/ 25 января 2011

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

Поскольку у вас уже есть слои (постоянство, рендеринг и т. д.), вы должны подумать об ответственности этих слоев.Например, вы можете GraphicsSystem принять Person и отобразить его на экране.Или PersonDAO, который отвечает за получение Person из базы данных.Или PersonPrintService, который знает, как печатать, и т. Д.

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

Выбранный вами подход (BusinessPersonClass, DataAccessPersonClass и т. Д.) Называется Шаблон декоратора .Ситуации, когда его применять, отличаются, но для меня лучшим примером этого шаблона является класс OutputStream Java, который может быть заключен в BufferedOutputStream, а затем снова заключен в FileOutputStream и т. Д.

НадеюсьНе знаю, какую логику BusinessPerson добавляет поверх Person, но для меня это выглядит так, что если есть люди, которые являются "деловыми людьми", вам лучше сделать BusinessPerson extension Person.

Я не могу больше комментировать, поскольку не знаю вашей проблемной области.

5 голосов
/ 19 января 2011

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

Что касается разделения по уровням, я не думаю, что вам следуетиметь BusinessPersonClass и DataAccessPersonClass.Вместо этого я считаю, что вам следует стремиться к следующему: ваш бизнес-уровень будет работать с типом IPerson: ему все равно, например, относится ли объект к типу Person или Salesman, он просто знает, что ожидает объект типа IPerson.Более того, бизнес-уровень - это то, где все бизнес-правила (естественно) - валидация и т. Д. Эти бизнес-правила, на мой взгляд, не должны быть частью объекта Person.Фактически, объект Person должен иметь дело только с атомарными операциями и свойствами одного человека - другими словами, он не должен содержать никакой бизнес-логики или логики доступа к данным.

То же самое относится и к уровню доступа к данным - он работает наIPerson, возвращает IPerson (ов), изменяет IPerson (ов).Уровень доступа к данным касается только операций CRUD, или как он добавляет, получает, обновляет, удаляет IPerson из / в источник данных.

Теперь, с пользовательским интерфейсом, я думаю, что это немного большеразные.Там обычно вы реализуете свои ViewModels (UIModels) и некоторые простые правила проверки (не касающиеся бизнес-логики, а достоверности ввода).Я могу думать о двух подходах:

1) Каждая модель представления пользовательского интерфейса, касающаяся Person, реализует IPerson.Таким образом вы обеспечиваете согласованность уровней и легко работаете с этими данными.

2) Каждая модель представления пользовательского интерфейса содержит свойство типа IPerson (как вы указали выше).На мой взгляд, это также хороший подход, поскольку он облегчает взаимодействие между пользовательским интерфейсом и бизнес-уровнем.

В целом, вот что я думаю:

уровень пользовательского интерфейса Имеет ссылку на IPerson тем или иным образом.Реализует основные правила проверки - достоверность ввода (действительно ли введенное электронное письмо является электронной почтой).Затем пользовательский интерфейс передает этот объект IPerson на бизнес-уровень для бизнес-правил и операций.

Бизнес-уровень Принимает объект IPerson, проверяет его с помощью бизнес-правил (действительно ли это электронное письмо уже существует всистема).Выполняет еще несколько операций бизнес-уровня, может быть, и вызывает уровень доступа к данным, например, для создания объекта.В этом примере бизнес-уровень вызовет функцию Create и передаст ей объект типа IPerson.

Уровень доступа к данным Операции CRUD над атомарными объектами - IPerson, IProduct и т. Д.

Надеюсь, это поможет.

3 голосов
/ 31 января 2011

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

Мне интересно, что я перешел от 0-го уровня (помещая все в файлы JSP) к 2-уровневому (сервлет-JSP) к уровню "бог знает сколько".Что я могу сказать по своему опыту, так это не слишком сходить с ума, придумывая дополнительные уровни, если вам это не нужно.Вы можете читать книги по программированию, требующие наличия этого слоя или этого слоя, но все это сводится к сложности вашего текущего проекта.

Преимущества наличия уровней заключается в продвижении уровня абстракций ... например, если вы решите поменять интерфейс вашего Struts с Spring MVC или JSF-фреймворком, то это не вызовет нежелательную отрицательную рябьэффекты для остальных ваших реализаций (базы данных, безопасности, ведения журналов и т. д.).Недостаток наличия слишком большого количества уровней заключается в том, что вы нежелательно вносите в свой проект большую сложность.Все слишком отвлечено, что вы теряете след, какой уровень делает то, что больше.Кроме того, если вы не реализуете уровни должным образом, вы в конечном итоге получите тесную связь между уровнями, и вы обнаружите, что модульное тестирование будет чрезвычайно сложным и разочаровывающим.Это связано с тем, что для того, чтобы вы могли выполнить модульное тестирование компонента A, вам сначала нужно будет инициализировать 10 других компонентов из других уровней ... и, наконец, вы не будете выполнять никакого модульного тестирования, потому что каждый раз, когда вы что-то меняете в своем коде,должны исправить все ваши тестовые случаи.Я сам пережил эту ошибку.

Чтобы сейчас все было просто, вам нужны следующие уровни: -

  • UI: это ваш зритель.Это может быть JSP, HTML, Freemarker и т. Д.
  • Model: содержит класс Person.Он содержит все свойства человека и метод getters / setters.Вот и все ... никакой бизнес-логики в этом.Это POJO.Вы используете это для обхода между уровнями.
  • Controller: Это ваш «авиадиспетчер».Он обрабатывает пользовательский запрос, он знает, какие сервисы нужно вызвать, чтобы выполнить работу, но здесь есть минимальные вычисления.Думайте о контроллере как о своем боссе.Босс всегда просит сотрудников делать то же самое, и, наконец, босс берет результат и представляет его пользователю через UI.
  • Service: здесь у вас есть PersonService,Он обрабатывает все услуги, связанные с человеком, например, создание нового пациента, поиск человека по фамилии и т. Д. Ваша служба является единственной, которая может взаимодействовать с базой данных.Он отобразит всю полученную информацию о человеке из базы данных в модель Person и вернет ее контроллеру.Если объем вашего проекта велик, возможно, вы захотите ввести уровень DAO для обработки всех сообщений, связанных с базой данных, но на данный момент уровень DAO не требуется, если вы не можете обосновать необходимость в этом.

Глядя на это ... первые 3 уровня - это ваш MVC, который очень часто используется при разработке веб-приложений.Уровень Service введен здесь для обеспечения возможности повторного использования кода между Controllers.Например, у вас есть несколько контроллеров, которые могут захотеть использовать PersonService для выполнения операции CRUD для человека.

Отсюда модульное тестирование становится очень простым.Поскольку Service является независимым уровнем и не зависит от каких-либо других уровней, вы можете легко написать контрольные примеры для проверки всего класса Service.После того, как вы проверили все свои сервисные API, тестирование Controller будет простым, потому что теперь вы можете смело издеваться над классом Service в контроллере, чтобы выполнить свой «счастливый путь» и «не такой счастливый».Путь "испытаний.Model на самом деле не требует никакого тестирования, потому что это просто POJO.Таким образом, вы можете легко достичь 95% тестового покрытия.

Там ... все довольны и готовы.:)

3 голосов
/ 30 января 2011

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

Пользовательский интерфейс: PersonView, PersonEditPage, NewPersonForm

Бизнес: Персона, День рождения, Адрес, Продавец, Сотрудник

Данные: соединение, база данных, таблица,Столбец

Таким образом, может произойти типичная последовательность операций в веб-приложении;

(screen)      UI        Business        Data      (storage)

Form/Page  PersonView   Organization    Connection     DB
   |           |             |                |         |
   |--type-+   |             |                |         |
   |       |   |             |                |         |
   |<------+   |             |                |         |
   |           |             |                |         |
   |---click-->|             |                |         |
   |           |--find(#3)-->|                |         |
   |           |             |-LoadPerson(3)->X         |
   |           |             |                X-select->X
   |           |             |                X         X
   |           |             |                X<--------X
   |           |             |<-----Person----|         |
   |           |<---Person---|                |         |
   |<---HTML---|             |                |         |
   |           |             |                |         |

Что здесь происходит?

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

onLinkClick() {
  personView.FindPerson(id:3);
  rendertoHtml();
}

PersonView теперь нужно найти человека, поэтому он запрашивает бизнес-уровень;он просит объект под названием «Организация» найти конкретного человека.Например,

PersonView.FindPerson(id) {
  organization.FindPerson(id)
}

Обратите внимание, что он спрашивает, не зная о базах данных, XML или веб-сервисах;он просто спрашивает бизнес-уровень напрямую: «Вы можете найти человека № 3?».Таким образом, уровень пользовательского интерфейса изолирован от уровня данных.

Затем бизнес-уровень (Организация) переходит на уровень данных и просит его загрузить запись о человеке.

Organization.FindPerson(id) {
  connection.LoadPerson(id);
}

Опять же, он не выполняет специфичные для данных запросы.Теперь мы получаем соединение, которое знает о конкретных деталях слоя данных;

Connection.LoadPerson(id) {
  connectDb();
  execute("SELECT name, dob FROM Person WHERE id =@id");
  return new Person(name, dob);
}

Итак, соединение определенно знает о конкретном механизме хранения (SQL), но имеетнет знаний об уровне пользовательского интерфейса, который вызвал его.Он возвращает объект Person, и Organization передает его обратно в PersonView, который затем знает, как генерировать HTML.

Как эта структура помогает?Представьте, что вы хотите перейти с файлов SQL на XML для хранения ваших данных.Вы можете создать другую реализацию Connection, которая использует xpath, например, так:

Connection.LoadPerson(id) {
  LoadXml();
  SelectNode("//person[@id=$(ID)]");
  return new Person(xml['name'], xml['dob']);
}

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

3 голосов
/ 28 января 2011

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


Извините, но мой английский отстой.Но я сделаю все возможное.: (


Вы используете n-уровневую архитектуру. Это означает, что ваше приложение большое (уровни и слои помогают только в том случае, если ваше приложение достаточно велико). N-ярус дает возможность изолировать некоторые вещиПример: ваша БД изменяется. Не берите в голову, как она меняется. Ваш уровень DAO может измениться, но ваш уровень Bussiness останется прежним. Мальчик, это огромно. Если у вас действительно большое приложение, это очень желательный момент.

Это означает n-уровень. Он изолирует один слой от другого. Изменения вашего пользовательского интерфейса не влияют на уровень данных и т. Д. Имейте это в виду. Хорошо, следующий шаг.

Теперьвы используете n-уровень, и каждый уровень / слой на самом деле является действительно большим «приложением». Ваш бизнес-уровень (или любой другой уровень) делает много вещей и имеет свои собственные объекты и домен. Здесь, внутри слоя,Вы полностью используете oop. Внутри уровня / слоя ваши объекты заботятся только о вашем домене уровня / уровня, вы можете связать объект с его собственной ответственностью. На бизнес-уровне Person делает только то, что Busiчеловек делает.В пользовательском интерфейсе ваш эквивалентный человек знает, как визуализировать свои данные или все, что вы хотите, чтобы пользователь делал в пользовательском интерфейсе.

Хорошо, еще один момент для уточнения.

Между слоями у вас есть интерфейс.Не интерфейс Java, интерфейс, подобный договору на эксплуатацию.В этом случае вы не используете oop.Вы передаете ДАННЫЕ, а не объекты, не логику, только ДАННЫЕ.Что это означает?Это означает, что что бы вы ни проходили через ваш слой / уровни, это не настоящий объект (или не должен быть).Зачем?Потому что вы не хотите, чтобы объект имел более одной ответственности, верно?

Вы видите мою точку зрения здесь?

Возможно, у вас есть класс Person.Но класс Person в Business отличается от уровня DAO и отличается для пользовательского интерфейса UNLESS Класс Person - это просто данные, перемещающиеся между слоями.

n-ярусы и oop не являются противоречивыми или соответствующими.Это не то же самое, что сравнивать.

1 голос
/ 31 января 2011

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

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

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

Я лично изменил представление о кругепредставляет «приложение», которое больше всего похоже на бизнес-уровень в типичной n-уровневой диаграмме.Затем я могу поменять механизмы персистентности (базу данных, XML-файл и т. Д.), Не затрагивая основной «прикладной» код.Я также могу поменять различные интерфейсы без изменения основного приложения.

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

Теперь кто-то упомянул сервис как отдельную концепцию.С точки зрения приложения как бизнес-логики я нахожу это проблематичным.Служба, если смотреть на нее с точки зрения бизнес-потока, представляет собой постоянный механизм.Инкапсулированная в сервисе модель домена, используемая сервисом «приложение», но при просмотре из вашего «приложения» вы чаще всего используете сервис для сохранения объектов.Возвращаясь к разделению бизнес-уровня и уровня data / persist, вы сможете поменять сервис для базы данных без огромных усилий.

Это всего лишь два моих цента иВаш пробег может варьироваться.

1 голос
/ 24 января 2011

Если я могу добавить здесь свои 0,02 доллара США, возможно, это немного не соответствует самому вопросу, но связано с чувством неправильного поведения.

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

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

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

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

Все они разные, и к ним следует относиться таким образом.

...