Здравствуйте, Enomay, Вы писали: E>возможно. но мне почему-то казалось что подсчетом стоимости заказов должен заниматься объект Order, а не OrderService.
А можно пояснить, почему вам так казалось?
1. Является ли подсчёт стоимости неотъемлемой частью поведения заказа?
2. Необходим ли для подсчёта стоимости заказа доступ к непубличной части его состояния?
3. Есть ли причины полагать, что для разных наследников класса Order будут реализованы разные способы подсчёта стоимости? (Реализацию моков в расчёт не берём — это артефакты процесса производства)
Вообще, в целом, какие конкретные преимущества даст внесение метода подсчёта стоимости заказа внутрь Order, по сравнению с размещением его снаружи? Желательно — конкретно. Ну там, если преимущество "гибкость" — то в чём именно, если "масштабируемость" — то каким образом она достигнется и т.д.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
E>>возможно. но мне почему-то казалось что подсчетом стоимости заказов должен заниматься объект Order, а не OrderService. S>А можно пояснить, почему вам так казалось? S>1. Является ли подсчёт стоимости неотъемлемой частью поведения заказа?
я считаю что стоимость заказа — это свойство самого заказа, и логично если оно будет находится в нем. расчет же этой стоимости происходит, грубо говоря, суммированием всех продуктов в заказе.
S>2. Необходим ли для подсчёта стоимости заказа доступ к непубличной части его состояния?
исходя из вышесказанного, ему не требуется доступ за пределы себя самого, все необходимое он имеет.
S>3. Есть ли причины полагать, что для разных наследников класса Order будут реализованы разные способы подсчёта стоимости? (Реализацию моков в расчёт не берём — это артефакты процесса производства)
нет, ордер есть и будет оставаться всегда одним. он не содержит хитрых алгоритмов, он лишь суммирует кол-во записей внутри себя. а вот какую цифру вернут эти записи, зависит исключительно от них. и это уже зависит от внутренней реализации.
S>Вообще, в целом, какие конкретные преимущества даст внесение метода подсчёта стоимости заказа внутрь Order, по сравнению с размещением его снаружи? Желательно — конкретно. Ну там, если преимущество "гибкость" — то в чём именно, если "масштабируемость" — то каким образом она достигнется и т.д.
тем, что мы имеем иерархию классов, приблизительно такую
Order — OrderLine — Product
каждое звено в цепочке несет ответственность за себя. продукт знает свою цену. он же может вернуть свою цену со скидкой, если его взяли более 2х штук. например. или потому что сегодня вторник. все зависит от условий.
но в данном случае для изменения расчета скидки на продукт, нам нужно будет изменить только класс конкретного продукта, а не перелопачивать весь метод сервиса, который в итоге обрастет кучей if/case.
а класс Order останется неизменным.
SRP как бы
Здравствуйте, Enomay, Вы писали:
E>>>в корпоративном секторе ни процедурного, ни функционального подхода для реализации БЛ нет. что очевидно. S>>Вы неправильно понимаете ООП. Анемик ничуть не менее ООП, чем рич. Вы просто ищете объекты не там, где надо.
E>возможно. но мне почему-то казалось что подсчетом стоимости заказов должен заниматься объект Order, а не OrderService.
Почему же обязательно должен ? Представь себе, что есть заказы в разных валютах, есть всякие скидки — на конкретные товары, у кастомера, у менеджера, сезонная и тд и тд.
Теперь получается, что такой объект Ордер должен знать про всё на свете — про курсы валют, про все сущности которые имеют отношение к скидками. В итоге получается нарушение инкапсуляции — изменение логики скажем скидок влияет на Ордер.
Конечно, подсчёт стоимости заказа можно спокойно оставить в Ордер, но только в том случае, если для этого нужны исключительно объекты вроде OrderItem.
I>Почему же обязательно должен ? Представь себе, что есть заказы в разных валютах, есть всякие скидки — на конкретные товары, у кастомера, у менеджера, сезонная и тд и тд.
I>Теперь получается, что такой объект Ордер должен знать про всё на свете — про курсы валют, про все сущности которые имеют отношение к скидками. В итоге получается нарушение инкапсуляции — изменение логики скажем скидок влияет на Ордер.
I>Конечно, подсчёт стоимости заказа можно спокойно оставить в Ордер, но только в том случае, если для этого нужны исключительно объекты вроде OrderItem.
там рядом есть мой ответ на этот счет. я не говорил что ордер содержит алгоритм расчета. но он знает как получить общую стоимость.
а как это будет выглядеть в случаи с внешними сервисами?
Здравствуйте, Lloyd, Вы писали:
L>>>Понимаешь в чем цимес, если выпячивать только эту сторону, то выходит, что процедурный подход более ООПшен, чем ОО, ибо в процедурах кроме поведения нет вообще ничего.
I>>А покажи-ка процедурную реализацию стека.
L>Сам осилишь: cтруктурка с указателем на вершину стека + пачка методов.
И где же здесь "более ООПшен, чем ОО" ? Ни по одной версии, Симула или Кея, это не будет ООП. Для Симуловской классики инкапсуляция отсутствует начисто, полиморфизм невозможен в принципе(разве что через void**). Для Кеевской нужна мессагинг + идентити.
Здравствуйте, Enomay, Вы писали:
I>>Конечно, подсчёт стоимости заказа можно спокойно оставить в Ордер, но только в том случае, если для этого нужны исключительно объекты вроде OrderItem.
E>там рядом есть мой ответ на этот счет. я не говорил что ордер содержит алгоритм расчета. но он знает как получить общую стоимость.
"он же может вернуть свою цену со скидкой, если его взяли более 2х штук. например. или потому что сегодня вторник"
"возьми два ведра джек дениэлс и получи третье бесплатно" как продукт сможет узнать, что он в заказе у конкретного кастомера, который заказал его дважды ?
"для изменения расчета скидки на продукт, нам нужно будет изменить только класс конкретного продукта"
А кто будет знать, где какие скидки ? Вот например скидка "возьми разных товаров на сумму > 100$" => "получи доставку бесплатно". Как это сделать в продукте ?
Ордеры, ордерайтемы, продукты остаются, зато скидки и вся логика вокруг продаж может меняться сколько угодно.
E>а как это будет выглядеть в случаи с внешними сервисами?
В случае с сервисами логика скидок может меняться как угодно независимо от сущностей в модели. Параметры для сервиса скидок можно хранить в базе, но только параметры. Объекты-сущености ничего не будут знать ни про скидки, ни про то, кто и как вычисляет скидку.
Здравствуйте, Ikemefula, Вы писали:
L>>Сам осилишь: cтруктурка с указателем на вершину стека + пачка методов.
I>И где же здесь "более ООПшен, чем ОО" ? Ни по одной версии, Симула или Кея, это не будет ООП. Для Симуловской классики инкапсуляция отсутствует начисто, полиморфизм невозможен в принципе(разве что через void**). Для Кеевской нужна мессагинг + идентити.
I>
Павел, читайте пост, на который отвечаете. Не в первый раз уже советуют.
Здравствуйте, Lloyd, Вы писали:
I>>И где же здесь "более ООПшен, чем ОО" ? Ни по одной версии, Симула или Кея, это не будет ООП. Для Симуловской классики инкапсуляция отсутствует начисто, полиморфизм невозможен в принципе(разве что через void**). Для Кеевской нужна мессагинг + идентити.
I>>
L>Павел, читайте пост, на который отвечаете. Не в первый раз уже советуют.
Ты слишком часто забываешь про что шла речь.
Ты сказал "процедурный подход более ООПшен, чем ОО" и я попопросил тебя продемонстрировать это на примере процедурного стека. Ты привел пример который даже с натягом не может считать ОО.
Потому снова та же просьба — покажи таки стек в процедурном подходе, что бы было очевидно, что он "более ООПшен? чем ОО"
Здравствуйте, Ikemefula, Вы писали:
L>>Павел, читайте пост, на который отвечаете. Не в первый раз уже советуют.
I>Ты слишком часто забываешь про что шла речь.
I>Ты сказал "процедурный подход более ООПшен, чем ОО"
Нет, Павел, я этого не говорил. Еще раз, Павел, повторюсь, не стоит вырывать слова из контекста, а уж из предложения и подавно не стоит.
E>>там рядом есть мой ответ на этот счет. я не говорил что ордер содержит алгоритм расчета. но он знает как получить общую стоимость. I>"возьми два ведра джек дениэлс и получи третье бесплатно" как продукт сможет узнать, что он в заказе у конкретного кастомера, который заказал его дважды ?
OrderLine знает кол-во этого продукта. он и передаст.
I>"для изменения расчета скидки на продукт, нам нужно будет изменить только класс конкретного продукта" I>А кто будет знать, где какие скидки ? Вот например скидка "возьми разных товаров на сумму > 100$" => "получи доставку бесплатно". Как это сделать в продукте ?
это уже будет в ордере.
I>Ордеры, ордерайтемы, продукты остаются, зато скидки и вся логика вокруг продаж может меняться сколько угодно.
E>>а как это будет выглядеть в случаи с внешними сервисами? I>В случае с сервисами логика скидок может меняться как угодно независимо от сущностей в модели. Параметры для сервиса скидок можно хранить в базе, но только параметры. Объекты-сущености ничего не будут знать ни про скидки, ни про то, кто и как вычисляет скидку.
ну, давайте на примере кода тогда, как все вышеперечисленное ляжет в него.
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, samius, Вы писали:
E>>>возьмем 10 процедур. 3 структуры. они друг с другом взаимодействуют. все это написано на Object Pascal. Будет ли это ООП? S>>Ответ на этот вопрос зависит не от количества процедур, структур, и не от языка, на котором выполнено взаимодействие. Построенное определенным образом взаимодействие вполне можно будет квалифицировать как ООП.
I>Это будет не ООП, а эмуляция ООП или ООП-стиль. Во всех букварях подробно объясняется, почему такой подход будет не ООП
Мне лень перечитывать все буквари, попробую угадать. Потому что метод вызывается не через точку/стрелочку?
Здравствуйте, samius, Вы писали:
I>>Это будет не ООП, а эмуляция ООП или ООП-стиль. Во всех букварях подробно объясняется, почему такой подход будет не ООП S>Мне лень перечитывать все буквари, попробую угадать. Потому что метод вызывается не через точку/стрелочку?
Сообщения, идентити, инкапсуляция... Где это всё ?
Здравствуйте, Enomay, Вы писали:
E>я считаю что стоимость заказа — это свойство самого заказа, и логично если оно будет находится в нем.
Это достаточно неожиданное предположение, которое, вообще говоря, нужно аргументировать.
Не всё, что можно рассчитать о заказе, стоит считать его свойством. Вот, скажем, среднеквадратичное отклонение количеств товарных позиций — это тоже "свойство самого заказа". Логично ли, если оно будет находиться в нём?
E>расчет же этой стоимости происходит, грубо говоря, суммированием всех продуктов в заказе.
Хм. Это очень интересный момент, вот это "грубо говоря". Сразу возникает вопрос — а когда это может быть не так?
Стоимость, о которой мы говорим, это уже с учётом скидок или нет? Если с учётом, то мы потенциально имеем циклическую зависимость — стоимость зависит от скидки, которая зависит от стоимости.
S>>2. Необходим ли для подсчёта стоимости заказа доступ к непубличной части его состояния? E>исходя из вышесказанного, ему не требуется доступ за пределы себя самого, все необходимое он имеет.
Вы отвечаете не на тот вопрос. Я спрашивал ровно обратное — требуется ли ему доступ "вовнутрь", к чему-то такому, что не видно снаружи.
S>>3. Есть ли причины полагать, что для разных наследников класса Order будут реализованы разные способы подсчёта стоимости? (Реализацию моков в расчёт не берём — это артефакты процесса производства)
E>нет, ордер есть и будет оставаться всегда одним. он не содержит хитрых алгоритмов, он лишь суммирует кол-во записей внутри себя. а вот какую цифру вернут эти записи, зависит исключительно от них. и это уже зависит от внутренней реализации.
Отлично. То есть производительность у нас тут гарантированно хреновая. Поясню на примере: если мы влезаем в один из OrderLine и меняем его quantity, то при сохранении нам нужно пересчитать OrderTotal. А для этого, в свою очередь, нужно материализовать все OrderLine, а не только ту, которая изменялась, и повызывать у каждой из них GetTotal(), который, в свою очередь, потребует материализовать Product(), чтобы позвать у него GetPrice(). То есть мы имеем как бы O(N) обращений к DAO на ровном месте.
Что характерно, никакой "логики" (то есть нетривиального поведения) ни у каких объектов здесь нет. Вся их работа сводится к тому, чтобы запретить программисту получать тот же результат через
select sum(quanity * p.price) from orderLine ol inner join product p on ol.productId = p.Id where orderLine.orderId = @orderID
S>>Вообще, в целом, какие конкретные преимущества даст внесение метода подсчёта стоимости заказа внутрь Order, по сравнению с размещением его снаружи? Желательно — конкретно. Ну там, если преимущество "гибкость" — то в чём именно, если "масштабируемость" — то каким образом она достигнется и т.д.
E>тем, что мы имеем иерархию классов, приблизительно такую
E>Order — OrderLine — Product
E>каждое звено в цепочке несет ответственность за себя. продукт знает свою цену. он же может вернуть свою цену со скидкой, если его взяли более 2х штук. например. или потому что сегодня вторник. все зависит от условий.
E>но в данном случае для изменения расчета скидки на продукт, нам нужно будет изменить только класс конкретного продукта, а не перелопачивать весь метод сервиса, который в итоге обрастет кучей if/case. E>а класс Order останется неизменным. E>SRP как бы
Отлично. Вы не только неправильно понимаете ООП, SRP вы тоже понимаете неверно.
Поясню:
1. Неотъемлемой обязанностью Order является только хранить список позиций и прочую информацию о заказе (покупателя, оформляюшего менеджера, и т.д). Вы засовываете в него ещё одну обязанность — подсчитывать сумму позиций.
Очевидно, что логика "подсчитать сумму позиций" будет применима не только к Order, но ещё много к чему. К накладной, счёт-фактуре, возвратной накладной, акту пересортицы, и прочему. Из-за того, что вы всунули эту логику внутрь заказа, вы будете вынуждены заниматься унылым Copy-Paste. Ну или мучительно думать, кого из этих классов от кого отнаследовать — в целом это изоморфно знаменитой проблеме "эллипс vs круг".
2. Продукт у вас, помимо обязанности "предоставлять информацию о продукте", стал внезапно обязан рассчитывать собственную стоимость.
Такой продукт крайне сложно реализовать.
Ваше предложение "вернуть свою цену со скидкой, если его взяли более 2х штук" просто прекрасно — тем, что непонятно как его реализовать. В продукте нет информации о том, сколько его взяли. Банальная схема "взявшему два третий бесплатно" в принципе нереализуема при такой иерархии объектов.
Уже пункта 1 достаточно для того, чтобы даже оставаясь в рамках заведомо тормозной и плохомасштабируемой Rich-ORM модели захотелось сделать что-то вроде
public interface IAccountableItem
{
decimal Total { get; }
}
public static class OrderUtils
{
public static GetTotal(this IEnumerable<IAccountableItem> items)
{
return items.Sum(item => item.Total);
}
}
То есть тут даже OrderService не нужен — это, вообще говоря, настолько банальная арифметика, что вставлять её в модель вообще пахнет оверархитектингом.
В пункте 2 мы понимаем, что задача "ввести в систему новую скидку" эквивалентна задаче "внести новый класс продукта". Вы думаете, что это гораздо легче, чем поменять класс типа OrderService.
А у вас уже продумана схема смены класса для существуюшего объекта? Буквари ООП стыдливо обходят этот вопрос молчанием. И правильно делают — в общем случае ничего хорошего ООП не предлагает.
OrderService имеет замечательную особенность — он Stateless. То есть мы можем его в любой момент заменить. Мы можем позволить старому и новому OrderService работать одновременно, до окончания всех транзакций, обрабатываемых OrderService. Поэтому любые изменения туда стоят значительно меньше, чем изменения в классе Product.
При этом вы почему-то думаете, что OrderService обрастёт каким-то набором "if-else" для расчёта скидки, как будто там запрещено использовать наследование, полиморфизм, и агрегацию.
На мой взгляд, всё как раз наоборот — в DiscountService у нас доступно всё. И история заказов конкретного кастомера, на основе которой мы можем перевести его в класс "ВИП клиентов" со своим набором скидок. И данные о количестве товаров каждого вида в данном заказе, чтобы реализовать "взявшему два по 0.5 Старого Мельника третья кружка бесплатно".
И данные о том, в какое время заказ был сделан и оплачен, чтобы реализовать happy hour.
А использовать эти данные при помощи if-else совершенно необязательно. Ведь есть же ООП. Десятки паттернов — визитор, стратегия, и прочее. Можно строить произвольные цепочки из бизнес-правил при помощи совсем небольшого набора правил по композиции их друг с другом. Это значительно удобее, чем писать всё в топорном стиле хранимых процедур.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, samius, Вы писали:
I>>>Это будет не ООП, а эмуляция ООП или ООП-стиль. Во всех букварях подробно объясняется, почему такой подход будет не ООП S>>Мне лень перечитывать все буквари, попробую угадать. Потому что метод вызывается не через точку/стрелочку?
I>Сообщения, идентити, инкапсуляция... Где это всё ?
Почем я знаю? Спроси себя. Я написал "определенное взаимодействие", ты что-то там выдумал и стал это выдуманное оспаривать.
Здравствуйте, Lloyd, Вы писали:
I>>Ты сказал "процедурный подход более ООПшен, чем ОО"
L>Нет, Павел, я этого не говорил. Еще раз, Павел, повторюсь, не стоит вырывать слова из контекста, а уж из предложения и подавно не стоит.
Процитированая фраза это отсылка к контексту Это же ты оспаривал формулировку "ООП — оно целиком про поведение" и в твоих словах "процедурный подход более ООПшен, чем ОО" это следствие. Потому я привел тебе пример, что бы продемонстрировать твою ошибку.
Раз уж ты любишь притворяться что непонял, то вот полная цитата:
"если выпячивать только эту сторону, то выходит, что процедурный подход более ООПшен, чем ОО, ибо в процедурах кроме поведения нет вообще ничего. "
"если выпячивать только эту сторону" — на примере стека — "то выходит" — а выходит то фигня какая то, которая и близко не ООП а у тебя каким то чудом вышло "процедурный подход более ООПшен, чем ОО"
Как ты вывел такое следствие — совершенно не ясно. Не подходит пример стека — возьми любой другой. В любом примере, где поведение зависит от предыстории сообщений придется забыть собственно про сообщения и явно приседать вокруг состояния.
Здравствуйте, samius, Вы писали:
I>>>>Это будет не ООП, а эмуляция ООП или ООП-стиль. Во всех букварях подробно объясняется, почему такой подход будет не ООП S>>>Мне лень перечитывать все буквари, попробую угадать. Потому что метод вызывается не через точку/стрелочку?
I>>Сообщения, идентити, инкапсуляция... Где это всё ? S>Почем я знаю? Спроси себя. Я написал "определенное взаимодействие", ты что-то там выдумал и стал это выдуманное оспаривать.
Ты написал, что в примере "возьмем 10 процедур. 3 структуры." можно реализовать не просто взаимодействие, а "можно будет квалифицировать как ООП". А как дошло до классификации, у тебя случилось "Почем я знаю?"
Из трёх структур и 10 процедур никакого взаимодействия которое "квалифицировать как ООП" не получится, максимум " эмуляция ООП или ООП-стиль". Это как например с подростками, которые любят одежду military style. такой стиль не делает их военными и даже не делает их одежду военной формой.
Здравствуйте, Ikemefula, Вы писали:
I>>>Ты сказал "процедурный подход более ООПшен, чем ОО"
L>>Нет, Павел, я этого не говорил. Еще раз, Павел, повторюсь, не стоит вырывать слова из контекста, а уж из предложения и подавно не стоит.
I>Процитированая фраза это отсылка к контексту
Фраза не была процитирована, она была вырвана из контекста и тем самым получила смысл, далекий от изначального.
I>Это же ты оспаривал формулировку "ООП — оно целиком про поведение" и в твоих словах "процедурный подход более ООПшен, чем ОО" это следствие.
Вы опять переврали, Павел. Я не оспарвал, что "ООП — про поведение", учитесь читать.
I>Потому я привел тебе пример, что бы продемонстрировать твою ошибку.
Ты читать сначала научись, потом будешь ошибки искать.
I>Раз уж ты любишь притворяться что непонял, то вот полная цитата: I>"если выпячивать только эту сторону, то выходит, что процедурный подход более ООПшен, чем ОО, ибо в процедурах кроме поведения нет вообще ничего. "
I>"если выпячивать только эту сторону" — на примере стека — "то выходит" — а выходит то фигня какая то, которая и близко не ООП
Эта "фигня" вполне соответствует приведенному определению ("ООП — оно целиком про поведение"). Если ты считаешь, что это фигня, то фигня — исключительно в определении, что и подтверждает дополнение этого определния, сделанное Sinclair-ом (идентичность).
I>а у тебя каким то чудом вышло "процедурный подход более ООПшен, чем ОО"
Нет, у меня вышло, что приведенное определение приодит к абсурдным выводам.
I>Как ты вывел такое следствие — совершенно не ясно.
Павел, если непонятно, откройте учебник логики для гуманитариев, обязательно полегчает.
I>Не подходит пример стека — возьми любой другой. В любом примере, где поведение зависит от предыстории сообщений придется забыть собственно про сообщения и явно приседать вокруг состояния.
Павел, вы сейчас с кем спорите, со мной или Sinclair-ом?
В проедложеной мною реалтзации стека есть как состояние, так и "сообщения". Я присел вокруг него настольно сильно, что аж выделил в отдельную сущность.
Хотя извини, постоянно забываю, что ты читать не умеешь.
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, samius, Вы писали:
I>>>>>Это будет не ООП, а эмуляция ООП или ООП-стиль. Во всех букварях подробно объясняется, почему такой подход будет не ООП S>>>>Мне лень перечитывать все буквари, попробую угадать. Потому что метод вызывается не через точку/стрелочку?
I>>>Сообщения, идентити, инкапсуляция... Где это всё ? S>>Почем я знаю? Спроси себя. Я написал "определенное взаимодействие", ты что-то там выдумал и стал это выдуманное оспаривать.
I>Ты написал, что в примере "возьмем 10 процедур. 3 структуры." можно реализовать не просто взаимодействие, а "можно будет квалифицировать как ООП". А как дошло до классификации, у тебя случилось "Почем я знаю?"
У меня случилось "почем я знаю" по поводу того, что ты обсуждаешь. Лично я не вижу никаких ограничений с тем что бы в 3х структурах и 10 процедурах организовать сообщения, идентити, инкапсуляцию, полиморфизм и не только это, и назвать это определенным взаимодействием.
I>Из трёх структур и 10 процедур никакого взаимодействия которое "квалифицировать как ООП" не получится, максимум " эмуляция ООП или ООП-стиль".
Я так не считаю I>Это как например с подростками, которые любят одежду military style. такой стиль не делает их военными и даже не делает их одежду военной формой.
Отличный съезд с темы
E>>я считаю что стоимость заказа — это свойство самого заказа, и логично если оно будет находится в нем. S>Это достаточно неожиданное предположение, которое, вообще говоря, нужно аргументировать. S>Не всё, что можно рассчитать о заказе, стоит считать его свойством. Вот, скажем, среднеквадратичное отклонение количеств товарных позиций — это тоже "свойство самого заказа". Логично ли, если оно будет находиться в нём?
отклонение чего от чего?
какое это отношение имеет к текущему пользовательскому заказу?
E>>расчет же этой стоимости происходит, грубо говоря, суммированием всех продуктов в заказе. S>Хм. Это очень интересный момент, вот это "грубо говоря". Сразу возникает вопрос — а когда это может быть не так? S>Стоимость, о которой мы говорим, это уже с учётом скидок или нет? Если с учётом, то мы потенциально имеем циклическую зависимость — стоимость зависит от скидки, которая зависит от стоимости.
не говорите глупости.
если скидка нужна от общей суммы, то мы можем её считать в методе GetTotal. если скидки высчитываются по какому-то хитрому алгоритму в зависимости от общей суммы, то используем стратегию.
S>>>2. Необходим ли для подсчёта стоимости заказа доступ к непубличной части его состояния? E>>исходя из вышесказанного, ему не требуется доступ за пределы себя самого, все необходимое он имеет. S>Вы отвечаете не на тот вопрос. Я спрашивал ровно обратное — требуется ли ему доступ "вовнутрь", к чему-то такому, что не видно снаружи.
он имеет доступ к внутренним коллекциям, а вот должны они быть видны из вне, или нет, зависит от задачи.
в любом случаи не он сам их создает.
E>>нет, ордер есть и будет оставаться всегда одним. он не содержит хитрых алгоритмов, он лишь суммирует кол-во записей внутри себя. а вот какую цифру вернут эти записи, зависит исключительно от них. и это уже зависит от внутренней реализации. S>Отлично. То есть производительность у нас тут гарантированно хреновая. Поясню на примере: если мы влезаем в один из OrderLine и меняем его quantity, то при сохранении нам нужно пересчитать OrderTotal. А для этого, в свою очередь, нужно материализовать все OrderLine, а не только ту, которая изменялась, и повызывать у каждой из них GetTotal(), который, в свою очередь, потребует материализовать Product(), чтобы позвать у него GetPrice(). То есть мы имеем как бы O(N) обращений к DAO на ровном месте. S>Что характерно, никакой "логики" (то есть нетривиального поведения) ни у каких объектов здесь нет. Вся их работа сводится к тому, чтобы запретить программисту получать тот же результат через S>
S>select sum(quanity * p.price) from orderLine ol inner join product p on ol.productId = p.Id where orderLine.orderId = @orderID
S>
Объект Order со всеми вложенностями может быть матириализован один раз, при изменении quantity у OrderLine, мы получим новую сумму заказа при вызове Order.GetTotal(), при этом мы не полезем в базу.
далее, материализация вложенного объекта выполняется 1м запросом, в данном случаи с 2мя джойнами, и лишь один раз.
тоесть, мы имеем производительность ни чем не хуже.
при этом, реализация БЛ производится на нормальном языке, а не в базе. что имеет массу своих преимуществ, о которых в интернете полно статей.
E>>но в данном случае для изменения расчета скидки на продукт, нам нужно будет изменить только класс конкретного продукта, а не перелопачивать весь метод сервиса, который в итоге обрастет кучей if/case. E>>а класс Order останется неизменным. E>>SRP как бы S>Отлично. Вы не только неправильно понимаете ООП, SRP вы тоже понимаете неверно. S>Поясню: S>1. Неотъемлемой обязанностью Order является только хранить список позиций и прочую информацию о заказе (покупателя, оформляюшего менеджера, и т.д). Вы засовываете в него ещё одну обязанность — подсчитывать сумму позиций. S>Очевидно, что логика "подсчитать сумму позиций" будет применима не только к Order, но ещё много к чему. К накладной, счёт-фактуре, возвратной накладной, акту пересортицы, и прочему. Из-за того, что вы всунули эту логику внутрь заказа, вы будете вынуждены заниматься унылым Copy-Paste. Ну или мучительно думать, кого из этих классов от кого отнаследовать — в целом это изоморфно знаменитой проблеме "эллипс vs круг".
это кто вам сказал что "Неотъемлемой обязанностью Order является только хранить список позиций и прочую информацию о заказе"?
вы, как и часть здешних, пихаете всё в базу, я правильно понял? тогда позиция определённо ясна.
кто же тогда должен считать сумму заказа? и почему это должен делать кто-то, а не сам заказ, при том что у него для этого есть все необходимые данные?
второй абзац ересь какая-то. вы бы хоть схему нарисовали, или класс накидали. а то выдумали что-то.
S>2. Продукт у вас, помимо обязанности "предоставлять информацию о продукте", стал внезапно обязан рассчитывать собственную стоимость. S>Такой продукт крайне сложно реализовать. S>Ваше предложение "вернуть свою цену со скидкой, если его взяли более 2х штук" просто прекрасно — тем, что непонятно как его реализовать. В продукте нет информации о том, сколько его взяли. Банальная схема "взявшему два третий бесплатно" в принципе нереализуема при такой иерархии объектов.
когда ордер будет считать стоимость, он спросит все свои OrderLine о их сумме, каждый OrderLine спросит о стоимости товара и помножит её на кол-во.
при этом, он спокойно передает это кол-во в товар, который вернет в зависимости от этого новую цену, уже со скидкой. все ж просто.
а вы усложняете.
S>Уже пункта 1 достаточно для того, чтобы даже оставаясь в рамках заведомо тормозной и плохомасштабируемой Rich-ORM модели захотелось сделать что-то вроде
S>
skipped
S>
S>То есть тут даже OrderService не нужен — это, вообще говоря, настолько банальная арифметика, что вставлять её в модель вообще пахнет оверархитектингом.
модель выполняет только свои обязанности.
вышенаписанное ни чем ни лучше. но это только вершина. а как будет расчитываться все остальное в иерархии?
S>В пункте 2 мы понимаем, что задача "ввести в систему новую скидку" эквивалентна задаче "внести новый класс продукта". Вы думаете, что это гораздо легче, чем поменять класс типа OrderService.
у вас есть 5 товаров. у каждого своя стратегия расчета скидок.
в моём случае, каждый товар сам знает свою стратегию. и мне придется изменить или 1 продукт, или добавить новый.
в вашем. все 5 стратегий в 1м методе. на сколько-то десятков строк. добавление/изменение всегда заставит менять этот метод. а это может повлиять на другие его части.
то есть один метод знает как считать скидки для совершенно разных объектов.
а это очень плохой подход.
S>А у вас уже продумана схема смены класса для существуюшего объекта? Буквари ООП стыдливо обходят этот вопрос молчанием. И правильно делают — в общем случае ничего хорошего ООП не предлагает.
существующего где?
S>OrderService имеет замечательную особенность — он Stateless. То есть мы можем его в любой момент заменить. Мы можем позволить старому и новому OrderService работать одновременно, до окончания всех транзакций, обрабатываемых OrderService. Поэтому любые изменения туда стоят значительно меньше, чем изменения в классе Product.
когда условий станет очень много, изменения сервиса будут сложнее. гораздо сложнее. но опять же, не видя код сервиса, сложно предположить с трудностях, с которыми придется столкнутся. но их будет не меньше, чем в моем случае.
S>При этом вы почему-то думаете, что OrderService обрастёт каким-то набором "if-else" для расчёта скидки, как будто там запрещено использовать наследование, полиморфизм, и агрегацию.
тоесть вы будете порождать тонные объектов и наделять их не свойственной для них логикой? ради бога.
S>На мой взгляд, всё как раз наоборот — в DiscountService у нас доступно всё. И история заказов конкретного кастомера, на основе которой мы можем перевести его в класс "ВИП клиентов" со своим набором скидок. И данные о количестве товаров каждого вида в данном заказе, чтобы реализовать "взявшему два по 0.5 Старого Мельника третья кружка бесплатно". S>И данные о том, в какое время заказ был сделан и оплачен, чтобы реализовать happy hour. S>А использовать эти данные при помощи if-else совершенно необязательно. Ведь есть же ООП. Десятки паттернов — визитор, стратегия, и прочее. Можно строить произвольные цепочки из бизнес-правил при помощи совсем небольшого набора правил по композиции их друг с другом. Это значительно удобее, чем писать всё в топорном стиле хранимых процедур.
конечно можно. так и делайте.
только я считаю что нет необходимости порождать ненужные сущности, если у нас уже имеются конкретные объекты, которые мы можем наделить свойственными только им обязанностями.
а если вы считаете что сумма заказа — это не обязанность заказа, ваше дело
хотите предметную дискуссию, можно описать задачу, и сделать 2 реализации, на основании которых и сравнивать.
Здравствуйте, Enomay, Вы писали:
E>>>там рядом есть мой ответ на этот счет. я не говорил что ордер содержит алгоритм расчета. но он знает как получить общую стоимость. I>>"возьми два ведра джек дениэлс и получи третье бесплатно" как продукт сможет узнать, что он в заказе у конкретного кастомера, который заказал его дважды ?
E>OrderLine знает кол-во этого продукта. он и передаст.
А откуда он узнает, какую скидку применять ? А если например "возьми два ведра джек дениэлс и получи __вёдра__ бесплатно", то как OrderLine узнает про вёдра ?
I>>"для изменения расчета скидки на продукт, нам нужно будет изменить только класс конкретного продукта" I>>А кто будет знать, где какие скидки ? Вот например скидка "возьми разных товаров на сумму > 100$" => "получи доставку бесплатно". Как это сделать в продукте ?
E>это уже будет в ордере.
Т.е. выходит что ордер должен знать обо всём в программе, начиная от скидок, конверсии валют до ролей юзеров, так что ли ?
А каким образом менять скидки ? Находить все ордеры, орделайны и тд и модифицировать их до тех пор пока ордер не будет оплачен и закрыт ?
E>>>а как это будет выглядеть в случаи с внешними сервисами? I>>В случае с сервисами логика скидок может меняться как угодно независимо от сущностей в модели. Параметры для сервиса скидок можно хранить в базе, но только параметры. Объекты-сущености ничего не будут знать ни про скидки, ни про то, кто и как вычисляет скидку.
E>ну, давайте на примере кода тогда, как все вышеперечисленное ляжет в него.
Можно даже без кода. В Order оставляем только OrderLine, никаких методов вроде вычисления суммы заказа там быть не должно.
var rates = new CurrencyRates();
var discountSvc = new DiscountService();
var orderSvc = new OrderService(discountSvc,rates);
var price = orderSvc.TotalPrice(order);
Теперь смотри. Внезапно оказывается, что фирме нужно включать в заказ еще и доставку, а её рассчет зависит от целой пачки факторов — дата-время доставки, адрес и тд и тд. Т.е. всунуть это в ордер не представляется возможным
var rates = new CurrencyRates();
var discountSvc = new DiscountService();
var deliverySvc = new DeliveryService();
var orderSvc = new OrderService(discountSvc,rates, deliverySvc);
var price = orderSvc.TotalPrice(order);
Т.е. логику вычисления стоимости заказа ожно модифицировать как угодно. Как быть, если вся она в классе Ордер ? Получается так, что класс Ордер будет жостко завязан на всех участников операции — скидки, доставка, валюта и тд и тд.
Как быть если фирма делегирует доставку другой фирме ? Опаньки, надо переписывать всё подряд. А вот в случае с сервисами нужно переписать только один сервис.