Здравствуйте, IT, Вы писали:
IT>Здравствуйте, снежок, Вы писали:
С>>Суть в том чтобы все get-ы и set-ы BO работали с DTO, а не приватными мемберами соответствующих свойств BO.
IT>Я вот никогда не понимал, в чём вообще смысл разделения на DTO и BO? Потому что Фаулер так сказал?
DTO — инкапсуляция работы с бд., методически — типа чтобы можно было другох хранилище использовать, практически — чтобы код для работы с бд не был разбросан по бизнес логике. Такое разделение снижает энтропию в системе (уменьшает зацепление), упращает написание unit тестов (они становятся типовыми) и значительно облегчает рефакторинг.
Если польза от разделения BO и BL вам не ясна — значит небыло еще в вашей жизни проекта, где такое разделение было бы критическим.
Здравствуйте, Аноним, Вы писали:
А>DTO — инкапсуляция работы с бд., методически — типа чтобы можно было другох хранилище использовать, практически — чтобы код для работы с бд не был разбросан по бизнес логике. Такое разделение снижает энтропию в системе (уменьшает зацепление), упращает написание unit тестов (они становятся типовыми) и значительно облегчает рефакторинг.
Вы с DAO не путаете?
А>Если польза от разделения BO и BL вам не ясна — значит небыло еще в вашей жизни проекта, где такое разделение было бы критическим.
А в чьей-то жизни такой проект был? Если в Вашей, то может поделитесь опытом откуда критичность возникла?
Здравствуйте, <Аноним>, Вы писали:
А>DTO — инкапсуляция работы с бд.,
Надо же, я всегда думал, что это называется DAL. Оказывается это тоже называется DTO. Буду знать.
А>Если польза от разделения BO и BL вам не ясна — значит небыло еще в вашей жизни проекта, где такое разделение было бы критическим.
Простите, был не прав.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>>>Гораздо хуже, если бы он делал быстро, но не то, что нужно. GZ>>Вот для этого и стоит определять что нам нужно быстро делать, что не столь важно, а на что вообще не стоит обращать внимание и бить разрабочика по ковырялкам если его заносит. IT>И в чём проблема? Мы же вроде как говорим о приоритетности одних требований над другими
Разница в том, что ты говоришь о некой приоритетности функциональных над нефункциональными требованиями. Я утверждаю, что важность требований не зависит от того — функциональное или нефункциональное оно.
IT>Ты говоришь совершенно о другом. Я говорил о приоритетах при принятии конкретных решений при реализации той или иной функциональности. Ты же пытаешься свалить всё в одну кучу и тем самым усложнить весь процесс. Меня этот вопрос сейчас вообще не интересует.
Нет. Это как раз и создает приоритет конкретных решений. Приоритет требований, их частота использования и риски — создают как раз последовательность и качество решений. Есть важные требования которые мы должны сделать в первую очередь, и при этом они работали достаточно эффективно. Есть менее важные требования, которые мы можем пока не учитывать а подумать о них по мере реализации важняка. А вот что такое приоритет требований мы и обсуждаем.
GZ>>
Функциональные требования — это достаточно строгая конструкция которая определяет сценарии/действия пользователя.
IT>Это примерно тоже самое, что "Функциональное требование — это предложение, записанное на бумаге". В таком смысле да, это строгая конструкция. Но любые функциональные требования базируются на предпочтениях человека их составялющих, а значит не могут быть чётко категоризированы.
Однако предпочтения клиента/аналитика точны и достаточны чтобы их точно выполнять. Мне как разработчику на это наплевать.
IT>К каким, например, категориям относятся требования к тёплости и мякгости?
А могут быть такие требования?
GZ>>Нефункциональные требования — все остальные требования к программе. IT>А как же концептуальные требования?
А что концепция? Концептуальные документы показывают нужность программы, ее абстактные цели, ограничивают задачу рамками. При разработке
IT>>>Например, сопровождаемость. GZ>>Сопровождаемость в разных задачах значат разные вещи. Что ты в данном случае имеешь ввиду под сопровождаемостью? IT>Готовность системы к будущим изменениям.
К каким изменениям? Для одних это подмена всего сервера, для других версионификация контракта, для третьих возможность добавления новых типов и т.д. и т.п. Low Coupling/High Cohesion — есть свойство любой правильной архитектуры.
IT>Так именно это и является целью — обеспечение интерфейса для внешних систем
Слишком абстрактно чтобы данное требование можно было выполнить.
IT>Меняется очень сильно. В зависимости от "замени, если не нравится" у тебя будет совсем другая реализация.
Повторю. Я писал данные требования для уровня сервера. "Пользователем" является клиентская программа.
Здравствуйте, IT, Вы писали:
IT>А зачем вообще нужно это разделение? По мне так оно выглядит очень натянуто.
Давай простой пример. Бизнес-объект User. Что с ним можно делать? Скажем сменить пароль, залогиниться, получить список пользователей. Итого у нас есть
На клиентской стороне
class BusinessObjectUser
{
public string SystemName { get; }
public string DisplayName { get; }
public void ChangePassword(string oldPassword, string newPassword);
public static BusinessObjectUser GetCurrentUser();
public static IList<BusinessObjectUser> GetUsers();
}
class BusinessObjectSystem
{
public BusinessObjectUser Login(string systemName, string password);
}
Но это совсем не то что мы посылаем серверу и принимем с сервера. А посылаем и принимаем мы вот что
struct DataTrasferUserChangePasswordRequest
{
public string SystemName;
public string OldPassword;
public string NewPassword;
}
struct DataTrasferUserLoginRequest
{
public string SystemName;
public string Password;
}
struct DataTrasferUserGetListResponseItem
{
public string SystemName;
public string DisplayName;
}
struct DataTrasferUserGetListResponse
{
public List<DataTrasferUserGetListResponseItem> Users;
}
А если посылать и принимать BO, то мало того что мы резко увеличит траффик, так ещё и нагрузим BO всяким мусором. Например в BusinessObjectUser пришлось бы завести write-only поля OldPassword и NewPassword которые бы почти никогда не использовались (но всегда гонялись по сети туда-обратно). Это не только неэффективно, но и приводит к трудно поддерживаемому коду с оттенком процедурного, а не ОО программирования, когда в один класс запихали то что нужно в разных местах.
Пример с User несколько экстремальный, но лично я всегда выделяю DTO. Особенно это важно в .Net, где можно "за компанию" незаметно для себя сериализовать кучу левый вещей, а потом играть в игру "какая клиентская сборка нужна на сервере". А ещё бывает, что на клиенте есть свой кеш, но храниться там не совсем то, что используеться и не совсем то, что получается с сервера.
Здравствуйте, adontz, Вы писали:
A>Но это совсем не то что мы посылаем серверу и принимем с сервера. А посылаем и принимаем мы вот что
А почему? Пример замечательный привёл. Так почему нельзя получить удалённую ссылку на опубликованный класс BusinessObjectUser и дёргать его методы? Зачем опускаться до обмена сообщениями? Ведь приведённые затем структуры мне представляются попыткой полезть на уровень ниже, где происходит обмен сообщениями между удалённым объектом и его прокси, и подменить их. Зачем?
Здравствуйте, akasoft, Вы писали:
A>Так почему нельзя получить удалённую ссылку на опубликованный класс BusinessObjectUser и дёргать его методы?
В веб-сервисах? Может, я что-то в этой жизни упустил, но я так не умею.
А Remoting через Интернет вообще не стоит использовать — пол мира за HTTP proxy сидят.
Здравствуйте, adontz, Вы писали:
A>Давай простой пример. Бизнес-объект User.
На самом деле, все выглядит немного не так... Для начала, в чем у нас проблема с объектом User? Проблема в том, что в него почему-то положили и бизнес-логику, то есть сделали как раз то, от чего Игорь предостерегал, да еще, помимо этого и UI-логикой нагрузили. На самом деле свойство DisplayName, видимо должно представлять собой какой-то из UI-ных методов, методы же GetCurrentUser() и GetUsers() — должны лежать в специальных классах бизнес-логики, которые знают, где брать пользователей вообще, и где брать текущих пользователей... Очевидно, сам-по себе бизнес-объект User подобными знаниями обладать не должен, так как подобное знание гвоздями его пришпилит к конкретному месту в системе или, говоря по модному, увеличит связность.
Далее ChngePassword. Этому методу так же место, скорее всего, в каком-нибудь UserManager-е, который и занимается насущным обслуживанием объекта User...
Вот после этих преобразований можно начинать прикручивать к Юзеру DTO. Надо ли это делать, учитывая что теперь в объекте User осталось? Вопрос для домашнего задания... =)
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, adontz, Вы писали:
A>>Давай простой пример. Бизнес-объект User. IB>На самом деле, все выглядит немного не так... Для начала, в чем у нас проблема с объектом User? Проблема в том, что в него почему-то положили и бизнес-логику, то есть сделали как раз то, от чего Игорь предостерегал, да еще, помимо этого и UI-логикой нагрузили. На самом деле свойство DisplayName, видимо должно представлять собой какой-то из UI-ных методов, методы же GetCurrentUser() и GetUsers() — должны лежать в специальных классах бизнес-логики, которые знают, где брать пользователей вообще, и где брать текущих пользователей... Очевидно, сам-по себе бизнес-объект User подобными знаниями обладать не должен, так как подобное знание гвоздями его пришпилит к конкретному месту в системе или, говоря по модному, увеличит связность. IB>Далее ChngePassword. Этому методу так же место, скорее всего, в каком-нибудь UserManager-е, который и занимается насущным обслуживанием объекта User... IB>Вот после этих преобразований можно начинать прикручивать к Юзеру DTO. Надо ли это делать, учитывая что теперь в объекте User осталось? Вопрос для домашнего задания... =)
Ну ты фактически используешь внутри программы DTO, а всю логику выносишь во внешние манипуляторы. А потом говоришь, что DTO не нужны
Здравствуйте, adontz, Вы писали:
A>Ну ты фактически используешь внутри программы DTO, а всю логику выносишь во внешние манипуляторы. А потом говоришь, что DTO не нужны
Bingo! =) Хоть горшком назови..
Собственно Игорев поинт в том и состоит, если я правильно понял, что уж коли BO и так практически не содержит логики, так почему бы и не использовать его в качестве DTO, вместо того, чтобы городить дополнительные конструкции и обзывать их разными умными словами...
Здравствуйте, IB, Вы писали:
IB>Bingo! =) Хоть горшком назови.. IB>Собственно Игорев поинт в том и состоит, если я правильно понял, что уж коли BO и так практически не содержит логики, так почему бы и не использовать его в качестве DTO, вместо того, чтобы городить дополнительные конструкции и обзывать их разными умными словами...
Ну с одной строны это хорошо, потому что переливания данных между объектами не будет. С другой плохо, потому что с инкапсуляция тю-тю. получаем обычное процедурное программирование. Ну и кроме того... как ты себе представляешь этот самый облегчённый объект User из моего примера? С двумя полями OldPassword и NewPassword? Хоть все методы оттуда выкинь, если есть передача частичных, неполных, нехранимых данных, то выделение DTO делает логику более чёткой, а протокол обмена менее избыточным. И всё ради того, чтобы избежать переливания данных между объектами? Мне кажется избыточность протокола больше повлияет на производительность.
class User
{
public int ID { get; }
public string SystemName { get; }
public string DisplayName { get; }
}
class UserManager
{
public void ChangePassword(int userID, string oldPassword, string newPassword);
public User GetUser(int userID);
public List<User> GetUserList();
}
Всего два класса. User не прибит гвоздями к источнику данных и может использоваться любой частью системы от UI до серверной бизнес логики. ChangePassword великолепно реализуется функцией с параметрами.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Всего два класса. User не прибит гвоздями к источнику данных и может использоваться любой частью системы от UI до серверной бизнес логики. ChangePassword великолепно реализуется функцией с параметрами.
Тут нет DTO или того что используется как DTO. Что шлёт ChangePassword серверу, где этот класс?
Здравствуйте, adontz, Вы писали:
IT>>Всего два класса. User не прибит гвоздями к источнику данных и может использоваться любой частью системы от UI до серверной бизнес логики. ChangePassword великолепно реализуется функцией с параметрами.
A>Тут нет DTO или того что используется как DTO. Что шлёт ChangePassword серверу, где этот класс?
class ClientUserManager
{
public void ChangePassword(int userID, string oldPassword, string newPassword);
public User GetUser(int userID);
public List<User> GetUserList();
}
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
A>>Тут нет DTO или того что используется как DTO. Что шлёт ChangePassword серверу, где этот класс?
IT>
IT>class ClientUserManager
IT>{
IT> public void ChangePassword(int userID, string oldPassword, string newPassword);
IT> public User GetUser(int userID);
IT> public List<User> GetUserList();
IT>}
IT>
Эээ, может я неудачно выразился. "Что шлёт ChangePassword серверу" имелось ввиду какие данные и в каком формате. Где класс хранитель тех данных, которые ChangePassword шлёт классу?
Здравствуйте, adontz, Вы писали:
A>Эээ, может я неудачно выразился. "Что шлёт ChangePassword серверу" имелось ввиду какие данные и в каком формате. Где класс хранитель тех данных, которые ChangePassword шлёт классу?
ChangePassword шлёт 3 переменных. Создвавать для этого специальный класс нет никакой необходимости.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
A>>Эээ, может я неудачно выразился. "Что шлёт ChangePassword серверу" имелось ввиду какие данные и в каком формате. Где класс хранитель тех данных, которые ChangePassword шлёт классу?
IT>ChangePassword шлёт 3 переменных. Создвавать для этого специальный класс нет никакой необходимости.
Если ты не выделил DTO в класс, это ещё не значит что у тебя совсем нет DTO, это значит что абстрактное понятие DTO реализовано в виде трёх переменных. А DTO всё равно есть, пусть и не так явно.
Здравствуйте, adontz, Вы писали:
A>Если ты не выделил DTO в класс, это ещё не значит что у тебя совсем нет DTO, это значит что абстрактное понятие DTO реализовано в виде трёх переменных. А DTO всё равно есть, пусть и не так явно.
Рома, это чушь, против которой я собственно говоря и протестую. Таким образом DTO можно обозвать всё что угодно, даже пакеты TCP. А назвав всё что можно DTO мы начинаем пихать его куда ни поподя уже не понимая нужно оно там или нет. Твой пример с ChangePassword это очень хорошо демонстрирует. Там где можно обойтись тремя параметрами ты создал целый класс. Класс здесь, класс там, а потом мы удивляемся почему сложность системы выходит из под контроля.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Рома, это чушь, против которой я собственно говоря и протестую. Таким образом DTO можно обозвать всё что угодно, даже пакеты TCP. А назвав всё что можно DTO мы начинаем пихать его куда ни поподя уже не понимая нужно оно там или нет. Твой пример с ChangePassword это очень хорошо демонстрирует. Там где можно обойтись тремя параметрами ты создал целый класс. Класс здесь, класс там, а потом мы удивляемся почему сложность системы выходит из под контроля.
Игорь, честное слово, я не идиот Я просто явно выделил DTO чтобы было точно понятно о чём я говорю, и только. В реальном проекте я конечно же создам вебметод с 3 параметрами, а не вебметод с одним параметром и вспомогательный класс.
Я просто не хотел приводить излишне громоздкие примеры, которые многим покажутся надуманными. Повторюсь — когда передются частичные данные, DTO в явном или не явном виде нужны.
Вот тебе такой пример. Пусть у User есть список некоторых именованных свойств (DAL ничего не знает об их смысле) которые хранят вспомогательные данные. Например свойство с именем "email" хранит адрес электронной почты. На клиенте у тебя будет
class User
{
public IDictionary<string, string> NamedProperties {get;}
}
Но когда я хочу просто получить список всех пользователей в группе Administrators мне глубоко наплевать на их номер ICQ и количество детей женского пола. С сервера вернётся список пользоватей, где для каждого пользователя будут личь частичные данные и гнать кучу классов с пустым Dictionary<string, string> нет решительно никакого смысла. Надо делать отдельный класс — DTO.