Здравствуйте, MozgC, Вы писали:
MC>А почему вы считаете что ответственность класса Order это только держать данные для заказа? Так можно начать про что угодно говорить. Давайте скажем что ответственность класса String — держать набор символов, а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength(), StringService.IndexOf(), StringService.StringToUpper() и т.д.
Есть обратный пример:
System.Convert
Ведь не сделали так:
Здравствуйте, stump, Вы писали:
S>Здравствуйте, C...R...a...S...H, Вы писали:
CRA>>Есть обратный пример: CRA>>System.Convert CRA>>Ведь не сделали так: CRA>>
CRA>>"1".ToInt32()
CRA>>
S>Руководствовались Single Responsibility Principle.
Странно, что тогда в String делают такие метод:
Intern
Format
Compare
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Здравствуйте, stump, Вы писали:
S>>Здравствуйте, C...R...a...S...H, Вы писали:
CRA>>>Есть обратный пример: CRA>>>System.Convert CRA>>>Ведь не сделали так: CRA>>>
CRA>>>"1".ToInt32()
CRA>>>
S>>Руководствовались Single Responsibility Principle. CRA>Странно, что тогда в String делают такие метод: CRA>Intern CRA>Format CRA>Compare
Ну ты даешь! Этож все статики. Мы тут копья ломаем за методы экземпляров
Здравствуйте, stump, Вы писали:
S>Ну ты даешь! Этож все статики. Мы тут копья ломаем за методы экземпляров
А для static отменили SRP?
Или они не входят в интерфейс типа?
Или можно делать так:
public class Math
{
public static void ShutDownPC(){}
}
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Здравствуйте, stump, Вы писали:
S>>Ну ты даешь! Этож все статики. Мы тут копья ломаем за методы экземпляров CRA>А для static отменили SRP? CRA>Или они не входят в интерфейс типа?
Статики это те же хелперы, только "заточенные" под свой родной класс, поэтому SRP к ним не прилепишь никаким боком.
Здравствуйте, stump, Вы писали:
S>То что ты, и Андрей называете внутренними неявными связями называется cohession (на русский обычно переводится как "зацепление").
In computer programming, cohesion is a measure of how strongly-related and focused the various responsibilities of a software module are.
Ни SRP, ни cohesion к текущему разговору вообще прямого отношения не имеют. Они имеют отношение к попыткам связать несколько классов в одну большую сущность с неясным назначением.
S> Действительно связность и зацепление находятся в некотором противоречии друг с другом. Большое зацеление так же плохо как и большая связность, но с ним борются другими методами.
А Буч, помнится, писал, что чем больше зацепление, тем лучше.
IB>>К слову, классический DI, если следовать твоей логике, увеличивает связность минимум в трое.. S>Да DI увеличивает связность
The technique through which the principle is applied is called Dependency injection. Objects in the lower layers are injected into the higher level objects. The result is that components are less tightly coupled, and there is a high degree of separation of concerns.
IB>>То есть, тот факт, что метод физически впихнули внутрь класса, автоматически делает его реализацию корректной? IB>>Я уже приводил пример к чему может привести подобный подход... S>Компилятору без разницы где лежит метод, внутри класса или вне класса.
А модификаторы доступа придумали трусы? И публично выставлять поля не рекомендуют, видать, от недостатка ума?
S>Надо приводить аргументы, а не "авторитетное мнение".
Аргументы тебе тоже привели в достаточном количестве.
S>>> плюс принцип single responsibility. IB>>Причем он тут? S>Да при том, что сериализация, сохранение в БД, UI не относятся к responsibility того класса, который мы обсуждаем.
Как узнал?
S> Но вообще-то для этого нужна практика, и критический подход ко всему что делаешь.
Давай ты не будешь оценивать квалификацию собеседника?
S> Форматирование для сериализации — это не свойственно для класса
Как узнал?
S>, поскольку это другой тип ответственности
Какой?
S>Дело в том что если foo() является функцией (в математическом смысле) исключительно только данных класса, то не зависимо от того, где ты ее разместишь, внутри самого класса, или вне, степень зависимости foo() от данных класса не изменяется.
А что тогда делает DI? Ведь при его применении связность уменьшается.
S> Это очевидно.
Нет, не очевидно. Более того, если бы это было верно, большинство базовых паттернов не имело бы смысла. Вот какой смысл, к примеру, в посетителе, если связность он, по твоему утверждению, не уменьшает?
S> Зато когда foo() находится внутри класса она входит в интерфейс самого класса и как ты говоришь, "любое внутреннее изменение, гарантировано не влияет на то, что находится за пределами контракта".
Тот же вопрос — какая разница, за чьим контрактом он находится, обрабатываемого класса или внешнего?
S> Если foo() лежит в другом классе, то появляется связь.
Ну то есть посетитель тоже увеличивает связность кода, так?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, MozgC, Вы писали:
MC>А почему вы считаете что ответственность класса Order это только держать данные для заказа?
А почему нет?
MC> Давайте скажем что ответственность класса String — держать набор символов, а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength()
Требует доступа к внутреннему состоянию.
MC>, StringService.IndexOf()
Вопросы производительности требуют доступа к внутреннему состоянию.
MC>, StringService.StringToUpper()
А вот тут правильно — ToUpper лучше было сделать статическим методом.
MC> и т.д.
Ага, можешь посмотнреть количество статических методов у System.String.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, stump, Вы писали:
S>Извини, но я не могу продолжать дискуссию в таком тоне. Ты передергиваешь.
Ну да, как я тебя поймал на откровенной неправде, так сразу начал передергивать. Можешь те моменты, в которых я якобы передергиваю, оставить без ответов. Какие именно, кстати?
S>К тому же, мой ответ предназначался не тебе.
Тогда почему ты его на емейл не отправил?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ромашка, Вы писали:
Р>Прелесть. Манагеры LOC считали, а IB теперь считает степень инкапсуляции Р>классов.
Идея, к сожалению, вовсе не моя.
Р>Ты лучше объясни мне, нафига оно надо? Ну, посчитали, и чего с ним делать-то?
Если мы вводим какой-то термин, значит надо уметь выделять что он описывает, иначе его введение бессмыслено.
Р>LOC тоже очень хорошо формализировался. Вот только толку от него было и есть ноль.
Просто ты не умеешь его готовить, впрочем это тема для совсем другого форума.
Здравствуйте, stump, Вы писали:
S>Давай ты не будешь передергивать и за меня делать выводы.
Я не передергиваю, я продолжаю твою мысль.
S>Я не понимаю что такое неявные связи внутри сущности.
Ну посмотри в википедию:
Some types of coupling, in order of highest to lowest coupling, are as follows:
Content coupling (high)
Content coupling is when one module modifies or relies on the internal workings of another module (e.g. accessing local data of another module).
Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module.
...
Это самый сильный (высокий) тип связности.
S>То что ты, и Андрей называете внутренними неявными связями называется cohession (на русский обычно переводится как "зацепление").
Ты ошибаешься.
S>Да DI увеличивает связность и это оправдано, поскольку он решает ряд других проблем.
DI связность уменьшает. Ты все-таки посмотри определение связности.
S>Компилятору без разницы где лежит метод, внутри класса или вне класса.
У тебя компилятор членам класса доступ к приватным переменным не дает?
S>Надо приводить аргументы, а не "авторитетное мнение".
Ты сам просил "авторитетного мнения", напомнить?
А как "авторитетное мнение" не устраивает, начинаешь анекдоты вспоминать?
Аргументов я привел более чем достаточно.
S>Да при том, что сериализация, сохранение в БД, UI не относятся к responsibility того класса, который мы обсуждаем.
Но они же все работают с состоянием? Очевидно, два твоих тезиса противоречят друг-другу.
S>В последовательности мне не откажешь
Похоже я поспешил..
S>Плохо, что не улавливаешь.
Ну так ты поясни...
S> Но вообще-то для этого нужна практика, и критический подход ко всему что делаешь.
Только давай вот практикой не меряться.. Ты простыми русскими словами объясни пожалуйста четкость своего критерия на основании которого ты решаешь, является ли метод членом класса или нет. Пока никакого формального критерия ты не изобразил.
S>Тут все очевидно,
Да?
S> класс создавался для представления каких-то там данных,
Класс создавался для хранения данных (хранения части состояния системы). Очевидно(!) рассчет — это совсем другая ответственность.
S>класс сообщает каков размер содержащихся в нем данных. Все это "свойственно" для данного класса.
По какой причине рассчет размера является "свойственным" для данного класса? На основании какого формального критерия ты так решил? Чем руководствовался?
S>Это SRP.
По мне, так это нарушение SRP.
S>Форматирование для сериализации — это не свойственно для класса,
Почему не свойственно-то? Еще раз, какой формальный критерий?
Доступ идет к одним и тем же данным, в одном и том же порядке, только логика при получении этих данных выполняется другая, но логика в любом случае должна быть инкапсулирована, так какая разница?
S>поскольку это другой тип ответственности (один у него уже есть, другой надо выносить во внешний класс).
Почему другой-то?
S>Спасибо,я давно понял твою аргументацию, но не могу с ней согласиться
Твое право..
S>Дело в том что если foo() является функцией (в математическом смысле) исключительно только данных класса, то не зависимо от того, где ты ее разместишь, внутри самого класса, или вне, степень зависимости foo() от данных класса не изменяется.
Изменится степень твоего контроля над этой зависимостью. Связность страшна не сама по себе, а тем, что ты ее не контролируешь.
S> Зато когда foo() находится внутри класса она входит в интерфейс самого класса и как ты говоришь, "любое внутреннее изменение, гарантировано не влияет на то, что находится за пределами контракта". То есть у нас все инкапсулировано.
Похоже ты все таки не понял мою аргументацию.. foo() уже не за пределами контракта, а значит любое изменение A может повлиять на foo() и наоборот.
S> Мы можем менять и внутренне устройство данных класса и реализацию foo() и никакие другие классы это не затронет.
Ну да, просто Foo() ачнет выдавать неверные результаты или падать в рантайме... )
S>Если foo() лежит в другом классе, то появляется связь.
Она и раньше была, просто ты ее не видел и значит не мог контролировать. У тебя логика маленького зверька, который считает, что если закрыть глаза, то и его никто не увидит..
S> Если нам понадобиться изменить алгоритм foo() то с большОй долей вероятности это выльется в изменение интерфейса класса A.
Да байта ради — это будут контролируеые и совершенно сознательные изменения. А вот если ты поменял A и забыл поменять foo(), что вполне возможно если их поместить вместе, то все будет гораздо печальнее.
S>Надеюсь, я достаточно подробно сформулировал свою точку зрения.
Угу, но свершенно неубедительно..
Здравствуйте, Ziaw, Вы писали:
Z>Аргумент про использование только публичного контракта плохо ложится на использование доменной модели,
Тем хуже для доменной модели.
Z>Значит единственным правильным решением является anemic domain model. POCO/POJO могут быть хороши в одних ситуациях, но при использовании такой модели меня не покидает мысль о том, что работа с ними ничем не отличается от использования DataSet'ов.
Очень сильно отличается — DataSet-ы пытаются инкапсулировать вместе с данными логику контроля состояния и кучу другой лабуды.
Здравствуйте, MozgC, Вы писали:
MC>А почему вы считаете что ответственность класса Order это только держать данные для заказа?
Потому что SRP. =) Если наделять его еще одной ответственностью, значит у класса будет две ответственности.
MC> Давайте скажем что ответственность класса String — держать набор символов,
Совершенно верно.
MC> а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength(), StringService.IndexOf(), StringService.StringToUpper() и т.д.
Если бы FCL проектировали сейчас, то именно так и сделали бы. Да и с самого начала сделали, если бы вовремя задумались... Собственно, на этот косяк им только ленивый не указывал.
MC>> Давайте скажем что ответственность класса String — держать набор символов, а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength() AVK>Требует доступа к внутреннему состоянию.
Это еще почему? Эту задачу вполне можно решить используя только публичный интерфейс класса string (без Lenght) перебором всех элементов, например. То что это решение неэффективно другой вопрос.
MC>>, StringService.IndexOf() AVK>Вопросы производительности требуют доступа к внутреннему состоянию.
Т.е. появились оговорки к "железному правилу"?
MC>>, StringService.StringToUpper() AVK>А вот тут правильно — ToUpper лучше было сделать статическим методом.
Лучше? По какому критерию? Уменьшению связности или удобству использования?
if (str.ToUpper().IndexOf("RSDN.RU") != -1)
// проитв if (StringHelper.ToUpper(str).IndexOf("RSDN.RU") != -1)
// пусть даже if (String.ToUpper(str).IndexOf("RSDN.RU") != -1)
Для стринга я выберу первое (удобство) и чхать мне на связность в этом конкретном случае. Как только связность станет проблемой -- буду думать, но это время может так и не наступить
Для какой другой задачи -- выберу внешний класс.
Для третьей, для которой неочевидно что предпочесть -- я подумаю 3-5 мин и выберу тот который интуитивно лучше (хз что это будет нужно смотреть по задаче).
В общем: этот вопрос из области философии программирования, а не архитектуры. Оба подхода довольно успешно дополняют друг друга.
Если "методов-сервисов" не много, то они спокойно уживутся в классе, если много, то хелперы (но не с таким ужасным названием) явно предпочтительнее. Эти два подхода можно скомбинировать -- часть в сам класс, часть в хелперы.
Здравствуйте, Aikin, Вы писали:
AVK>>Требует доступа к внутреннему состоянию. A>Это еще почему?
А откуда еще взять длину строки?
A> Эту задачу вполне можно решить используя только публичный интерфейс класса string (без Lenght) перебором всех элементов, например.
Нельзя — перформанс уже не тот.
A> То что это решение неэффективно другой вопрос.
Нет, не другой, а этот самый.
AVK>>Вопросы производительности требуют доступа к внутреннему состоянию. A>Т.е. появились оговорки к "железному правилу"?
Никаких оговорок. Приемлемая производительность не может быть реализована толькопубличным контрактом — метод нельзя помещать во внешний класс.
AVK>>А вот тут правильно — ToUpper лучше было сделать статическим методом. A>Лучше? По какому критерию?
По тому, который в этом треде обсуждается.
A>Для стринга я выберу первое (удобство)
Для удобства умные люди придумали extension methods.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
IB>Тем хуже для доменной модели.
Итак имеем модель:
public class OrderLine
{
public Order Order { get; set; }
}
public class Order
{
public IList<OrderLine> OrderLines { get; set; }
public void AddOrderLine(OrderLine line)
{
line.Order = this;
OrderLines.Add(line);
}
}
AddOrderLine следует вынести в статик хелпер?
А в данном случае нет?
public class Order
{
private IList<OrderLine> orderLines;
public void AddOrderLine(OrderLine line)
{
line.Order = this;
orderLines.Add(line);
}
public IEnumerable<OrderLine> OrderLines
{
get
{
foreach (var line in orderLines)
yield return line;
}
}
}
Здравствуйте, IB, Вы писали:
MC>> а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength(), StringService.IndexOf(), StringService.StringToUpper() и т.д. IB>Если бы FCL проектировали сейчас, то именно так и сделали бы.
Зачем?
IB>Да и с самого начала сделали, если бы вовремя задумались... Собственно, на этот косяк им только ленивый не указывал.
А в чем косяк? Только без абстрактной теории, пожалуйста, на практике в чем косяк?