2008-08-12 17 views
11

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

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

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

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

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

ответ

7

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

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

например

public class User 
{ 
    private string name; 
    private AccountStatus status; 

    private User() 
    { 
    } 

    public string Name 
    { 
     get { return name; } 
     set { name = value; } 
    } 

    public AccountStatus Status 
    { 
     get { return status; } 
    } 

    public void Activate() 
    { 
     status = AccountStatus.Active; 
    } 

    public void Suspend() 
    { 
     status = AccountStatus.Suspended; 
    } 

    public static User GetById(int id) 
    { 
     User fetchedUser = new User(); 

     // Lots of database and error-checking code 
     // omitted for clarity 
     // ... 

     fetchedUser.name = (string) reader["Name"]; 
     fetchedUser.status = (int)reader["statusCode"] == 0 ? AccountStatus.Suspended : AccountStatus.Active; 

     return fetchedUser; 
    } 

    public static void Save(User user) 
    { 
     // Code to save User's internal structure to database 
     // ... 
    } 
} 

В этом примере мы имеем объект, представляющий пользователя с именем и AccountStatus. Мы не хотим, чтобы статус был задан напрямую, возможно, потому, что мы хотим проверить, является ли изменение действительным переходом статуса, поэтому у нас нет сеттера. К счастью, код сопоставления в методах GetById и Save static имеет полный доступ к имени и статусу объекта.

Второй вариант - иметь второй класс, который отвечает за отображение. Это имеет то преимущество, что разделяет различные проблемы бизнес-логики и настойчивости, которые могут позволить вашему дизайну быть более проверяемым и гибким. Проблема с этим методом заключается в том, как открыть поля имени и статуса внешнему классу. Некоторые варианты: 1. Используйте рефлексию (которая не имеет никаких сомнений в том, чтобы копать глубоко в частных частях вашего объекта) 2. Предоставляйте специально названные публичные сеттеры (например, прикрепите их к слову «Частный») и надейтесь, что никто их не использует случайно 3. Если ваш язык поддерживает его, сделайте сеттеры внутренними, но предоставите доступ к модулю модуля данных. Например. используйте InternalsVisibleToAttribute в .NET 2.0 и далее или друг функции в C++

Для получения дополнительной информации, я рекомендовал бы классическую книгу Мартина Фаулера «Узоры архитектуры предприятия»

Однако, как слово предупреждения, прежде чем идти по пути написания собственных которые я настоятельно рекомендовал бы использовать сторонний инструмент реляционного картографирования объекта (ORM), такой как nHibernate или Microsoft Entity Framework. Я работал над четырьмя различными проектами, где по разным причинам мы написали собственный картограф, и очень легко тратить много времени на сохранение и расширение карты, вместо того, чтобы писать код, который обеспечивает конечное значение пользователя. Я использовал nHibernate в одном проекте до сих пор, и хотя изначально он имеет довольно крутую кривую обучения, инвестиции, которые вы вкладываете в начале, значительно окупаются.

1

Я всегда создаю отдельную сборку, которая содержит:

  • Много небольших интерфейсов (думаю ICreateRepository, IReadRepository, IReadListRepsitory .. список можно продолжать, и большинство из них в значительной степени зависит от дженериков)
  • A много конкретных интерфейсов, таких как IPersonRepository, которые наследуются от IReadRepository, вы получаете точку.
    Все, что вы не можете описать с помощью только меньших интерфейсов, вы помещаете в конкретный интерфейс.
    До тех пор, пока вы используете IPersonRepository для объявления своего объекта, вы получаете чистый, согласованный интерфейс для работы. Но кикер, вы также можете сделать класс, который принимает f.x. ICreateRepository в своем конструкторе, поэтому код будет очень легко сделать некоторые действительно фанковые вещи. Здесь также есть интерфейсы для Сервисов бизнес-уровня.
  • Наконец, я вставляю все объекты домена в дополнительную сборку, просто чтобы сделать базу кода более чистым и более слабо связанным. Эти объекты не имеют никакой логики, они всего лишь общий способ описания данных для всех слоев 3+.

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

0

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

public class BusinessObjectRecord : BusinessObject 
{ 
} 
0

Что вы имеете в виду, что уровень данных не должны быть в курсе бизнес-логики? Как бы вы заполнили бизнес-объект данными?

Я часто делаю это:

namespace Data 
{ 
    public class BusinessObjectDataManager 
    { 
     public void SaveObject(BusinessObject object) 
     { 
       // Exec stored procedure 
     { 
    } 
} 
5

Это классическая проблема - разделяя вашу модель предметной области от модели базы данных.Есть несколько способов атаковать его, это действительно зависит от размера вашего проекта, на мой взгляд. Вы можете использовать шаблон репозитория, как говорили другие. Если вы используете .net или java, вы можете использовать NHibernate или Hibernate.

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

пс название вопроса немного неправильно ведущих

1

@ Ice ^^ Heat:

Что вы имеете в виду, что уровень данных не должны быть в курсе бизнес-логики ? Как бы вы заполнили бизнес-объект данными?

Пользовательский интерфейс запрашивает ServiceClass в бизнес-уровне для предоставления услуги, а именно получение списка объектов, отфильтрованных объектом с необходимыми данными параметров.
Затем ServiceClass создает экземпляр одного из классов репозитория в уровне данных и вызывает GetList (фильтры ParameterType).
Затем уровень данных обращается к базе данных, извлекает данные и сопоставляет их с общим форматом, определенным в сборке «домен».
BL не имеет больше работы с этими данными, поэтому он выводит его в пользовательский интерфейс.

Затем пользовательский интерфейс хочет отредактировать элемент X. Он отправляет элемент (или бизнес-объект) в службу в бизнес-уровне. Бизнес-уровень проверяет объект, и если он в порядке, он отправляет его на уровень данных для хранения.

Пользовательский интерфейс знает сервис в бизнес-уровне, который снова знает об уровне данных.

Пользовательский интерфейс отвечает за сопоставление входных данных пользователей с объектами и от них, а уровень данных отвечает за сопоставление данных в db с объектами и из них. Бизнес-уровень остается чисто деловым. :)

0

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

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

public class BusinessLayer : ISimpleBusiness 
{} 

public class Some3LayerObject : ISimpleSome3LayerObject 
{} 
0

Вы можете разделить ваши интерфейсы на два типа, а именно:

  • Посмотреть интерфейсы - которые являются интерфейсами, которые определяют ваше взаимодействие с UI и
  • интерфейсы данных - которые являются интерфейсами, которые позволят вам указать взаимодействие с вашими данными.

Возможно наследовать и реализовать оба набора интерфейсов таким образом, чтобы:

public class BusinessObject : IView, IData 

Таким образом, на вашем уровне данных вам нужно будет только увидеть реализацию интерфейса IData, тогда как в пользовательском интерфейсе вам нужно будет увидеть реализацию интерфейса IView.

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

public class BusinessObject : DomainObject 

public class ViewManager<T> where T : DomainObject 

public class DataManager<T> where T : DomainObject 

Это, в свою очередь, позволяет ваш бизнес-объект, чтобы остаться не зная как слой UI/View, так и слой данных.

0

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

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

Запишите хранимые процедуры для инкапсуляции данных. Используйте набор результатов, DataSet, DataTable, SqlCommand (или java/php/любой эквивалент) по мере необходимости из кода для взаимодействия с базой данных. Эти объекты вам не нужны. Отличным примером является внедрение SqlDataSource в страницу .ASPX.

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

Объектно-ориентированные преобразователи - это дьявол. Прекратите использовать их.

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

+0

Я полагаю, это может сработать, если все, что вы делаете, - это положить экран поверх таблицы базы данных. – 2008-10-01 20:45:50

 Смежные вопросы

  • Нет связанных вопросов^_^