Помогите с кажущимся противоречием двух понятий - разработка oop и n-level

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

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

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

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

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

Ответ 1

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

ok, я могу быть избыточным здесь, но я думаю, что есть какой-то момент:


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


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

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

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

Хорошо, еще один момент для разъяснения.

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

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

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

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

Ответ 5

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

UI: 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 [email protected]");
  return new Person(name, dob);
}

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

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

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

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

Ответ 6

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

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

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

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

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

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

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

Там... все счастливо и сделано.:)

Ответ 7

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

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

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

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

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

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

Ответ 8

Нет ничего плохого в том, что у разных моделей разные "уровни" и сопоставление между ними. На самом деле многие модели поощряют это.

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

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

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

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

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

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