Есть класс, который реализует некую ф-ть
class Object
У него есть методы, типа
GetName — имя
GetSize — возвращает свой размер без учета детей
GetChilds — возвращает список своих детей
Появилась необходимость написать ряд методов, типа расчета размера рекурсивно с учетом всех детей, генерации уникального имени среди детей и т.д
Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется. Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво...
Кто как поступает с такими методами?
L>Сваливаем в отдельный ObjectHelper
Главное не забыть задать тот же самый вопрос позднее, когда ObjectHelper превратится в свалку
Т.е. как временное решение этот способ очень даже хорош. А тем более если проповедовать принцип "делать только то, что нужно в данный момент". Вполне возможно, позднее кандидат на "хозяина" этим методам найдеться сам.
Re: Куда девать ф-ции внешние для класса
От:
Аноним
Дата:
19.07.08 10:09
Оценка:
Здравствуйте, Аноним, Вы писали:
А>Появилась необходимость написать ряд методов, типа расчета размера рекурсивно с учетом всех детей, генерации уникального имени среди детей и т.д А>Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется. Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво... А>Кто как поступает с такими методами?
Ситуация нечастая, я обычно делаю отдельный статический класс с набором таких функций. Но это, видимо, не совсем лучший выход.
Здравствуйте, Aikin, Вы писали:
L>>Сваливаем в отдельный ObjectHelper A>Главное не забыть задать тот же самый вопрос позднее, когда ObjectHelper превратится в свалку
Очень интересно, почему ObjectHelper должен превратиться в свалку?
L>>>Сваливаем в отдельный ObjectHelper A>>Главное не забыть задать тот же самый вопрос позднее, когда ObjectHelper превратится в свалку CRA>Очень интересно, почему ObjectHelper должен превратиться в свалку?
Ок, "если вдруг превратится в свалку". Ну есть у меня подозрения (и даже примеры из опыта) что такое может случиться (с приличной вероятностью).
Само название этому способствует: "Helper". Помогает, ну просто во всем-всем-всем помогает. А позже, обнаруживаешь, что этот Помощник, не просто "помощник", а Помощник в нескольких совершенно конкретных направлениях. И оказывается, что его можно разделить на несколько классов с Бизнесс-Названиями: ObjectSizeCalculator, ObjectChildrenManager, ...
Главное не упустить этот момент.
P.S. Кста, то же самое можно сказать про суффикс "Manager".
P.P.S. Это не более чем мое ИМХО и навязывать его я никому не хочу
А>Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется.
Мне все же кажется, что методам CalcFullObjectSize и GetObjectUniqChildName самое место в Object
Плюсы:
— Не плодится дополнительный объект с непонятной зоной ответственности (об этом говорит имя объекта) // имхо, самый главный плюс
— Сам класс может более эффективно решить эти задачи, так как знает свою внутреннюю структуру
— Требуемый для подсчета публичный интерфейс может в будущем измениться // самый неглавный плюс
Минусы:
-- Нарушается принцип "сингл респонцибилити".? ИМХО, это в ответственности класса знать свои "внутренности"
Object -- (скорее всего) пример паттерна Composite.
Если посмотреть на примеры реализации этого паттерна (System.Windows.Forms.Control, XmlDocument, ...) можно увидеть, что операции над своими составляющими чаще всего поручаются именно Композиту, а не внешнему для него классу.
Здравствуйте, Aikin, Вы писали:
L>>Сваливаем в отдельный ObjectHelper A>Главное не забыть задать тот же самый вопрос позднее, когда ObjectHelper превратится в свалку
A>Т.е. как временное решение этот способ очень даже хорош. А тем более если проповедовать принцип "делать только то, что нужно в данный момент". Вполне возможно, позднее кандидат на "хозяина" этим методам найдеться сам.
Я, например, считаю, что работа по этому самому принципу "делать только то, что нужно в данный момент", часто встречаемая практика,
подобна работе по жадному алгоритму — результат-то вы получите, но для некоторого класса задач не оптимальный.
Здравствуйте, Аноним, Вы писали:
А>Есть класс, который реализует некую ф-ть А>class Object А>У него есть методы, типа А>GetName — имя А>GetSize — возвращает свой размер без учета детей А>GetChilds — возвращает список своих детей
А>Появилась необходимость написать ряд методов, типа расчета размера рекурсивно с учетом всех детей, генерации уникального имени среди детей и т.д А>Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется. Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво... А>Кто как поступает с такими методами?
Я бы добавил в класс, по сути это методы объекта (если переименовывать — GetFullObjectSize и GetObjectUniqChildName),
просто это будут не простые геттеры, а скрывающие какую-то логику — затем поля и делают доступными через get/set, потому что там может не "return fullObjectSize;".
При таком решении результаты работы методов можно будет кэшировать и, если объект не изменился, выдавать закэшированное значение.
Метод CalcFullObjectSize можно сделать статическим и вызывать его из GetFullObjectSize.
Здравствуйте, Аноним, Вы писали:
А>Появилась необходимость написать ряд методов, типа расчета размера рекурсивно с учетом всех детей, генерации уникального имени среди детей и т.д А>Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется. Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво...
Действительно не красиво. То что методы могут быть реализованы за счет публичного интерфейса класса вовсе не повод выносить их в отдельный класс. Не следует забывать что объект инкапсулирует не только свое состояние но и поведение, т.е. код. Это собственно и называется инкапсуляцией (данные + код).
В хелпер класс можно выносить аспекты, не свойственные для самого класса. Например, код форматирования данных класса для сериализации.
Здравствуйте, stump, Вы писали:
S>Здравствуйте, Аноним, Вы писали:
S>Действительно не красиво. То что методы могут быть реализованы за счет публичного интерфейса класса вовсе не повод выносить их в отдельный класс. Не следует забывать что объект инкапсулирует не только свое состояние но и поведение, т.е. код. Это собственно и называется инкапсуляцией (данные + код). S>В хелпер класс можно выносить аспекты, не свойственные для самого класса. Например, код форматирования данных класса для сериализации.
Stump, по вашим словам, получается, что метод "генерации уникального имени среди детей" — это подходящий кандидат на инкапсуляцию в Object?
Мне например кажется, что рекурсивный обход дерева, это не ответственность самого дерева
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Stump, по вашим словам, получается, что метод "генерации уникального имени среди детей" — это подходящий кандидат на инкапсуляцию в Object? CRA>Мне например кажется, что рекурсивный обход дерева, это не ответственность самого дерева
Ну с чего ты взял, что "генерации уникального имени среди детей" == "рекурсивный обход дерева" ?
Но даже если так, дети — это данные объекта. При рекурсивном обходе дерева детей используются какие либо внешние данные? Очевидно нет. И это веский повод инкапсулировать этот метод в данный класс.
А вот другой пример. Во многих бизнес приложениях есть функциональность, позволяющая настраивать правила нумерации экземпляров сущностей (заказов, накладных и т.п.). Вот это уже повод вынести данную функциональность за пределы сущности. Правда тут понадобится уже не хелпер а что-то вроде strategy или какой нибудь другой IoC.
S>Ну с чего ты взял, что "генерации уникального имени среди детей" == "рекурсивный обход дерева" ?
Я так не считаю
S>Но даже если так, дети — это данные объекта. При рекурсивном обходе дерева детей используются какие либо внешние данные? Очевидно нет. И это веский повод инкапсулировать этот метод в данный класс.
Очень интересная тем, может ли сам алгоритм обхода быть внешними данным, а считаю что ДА, так как таких алгоритмов может быть нескально, например: обход начиная с родителя, либо обход начиная с самого нижнего уровня.
Из этого следует что, может существовать функциональность,которая требует различные алгоритмы обхода, следовательно, как написано Вами ниже необходимо создавать strategy или реализовывать IoC.
S>А вот другой пример. Во многих бизнес приложениях есть функциональность, позволяющая настраивать правила нумерации экземпляров сущностей (заказов, накладных и т.п.). Вот это уже повод вынести данную функциональность за пределы сущности. Правда тут понадобится уже не хелпер а что-то вроде strategy или какой нибудь другой IoC.
Выше я рассмотрел пример с алгоритмом обходом дерева (который, является кандидатом на инкапсуляцию) и почему то, я не вижу смысла инкапсулировать в сам объет несколько алгоритмов обхода. И по этому получается придется выносить данный алгоритм в другой класс, а не инкапсулировать. А вот как называть новый класс TreeBypassStategy реализуя стратегю, либо ObjectHelper не реализую оную. Да хоть ObjectManagerService. Главное, то, что алгоритм отделе от Object.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Здравствуйте, stump, Вы писали:
S>>Ну с чего ты взял, что "генерации уникального имени среди детей" == "рекурсивный обход дерева" ? CRA>Я так не считаю
S>>Но даже если так, дети — это данные объекта. При рекурсивном обходе дерева детей используются какие либо внешние данные? Очевидно нет. И это веский повод инкапсулировать этот метод в данный класс. CRA>Очень интересная тем, может ли сам алгоритм обхода быть внешними данным, а считаю что ДА, так как таких алгоритмов может быть нескально, например: обход начиная с родителя, либо обход начиная с самого нижнего уровня. CRA>Из этого следует что, может существовать функциональность,которая требует различные алгоритмы обхода, следовательно, как написано Вами ниже необходимо создавать strategy или реализовывать IoC.
Я говорю здесь об инкапсуляции данных и кода, которой зачастую необоснованно пренебрегают. Увлечение "хелпер" классами хороший пример подобного пренебрежения. Действительно, бывает масса обстоятельств, которые затрудняют подобную инкапсуляцию, и тогда хэлпер представляется наиболее простым решением в лоб. Но в большинстве случаев самое простое решение не самое удачное. Тут говорили, что хэлпер может превратится в помойку, но чаще возникает другая проблема. По мере усложнения кода хэлпера, он начинает влиять на интерфейс класса которому он "помогает". Хэлперу нужно все больше и больше знать о внутреннем состоянии объекта которому он "помогает".
Хелперы увеличивают связность и размазывают логику. В тоже время, часто для таких случаев IoС, например dependency ijection, более удачное решение, поскольку не нарушает инкапсуляцию и порождает меньшую свяность.
Поэтому прежде создавать хэлпер надо (1) хорошенько подумать можно ли этот код оставить в самом классе, и (2) если оставить его там нельзя стоит прикинуть, нет ли более подходящего варианта для этого (observer, service locator и т.п.).
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Здравствуйте, stump, Вы писали:
S>>Ну с чего ты взял, что "генерации уникального имени среди детей" == "рекурсивный обход дерева" ? CRA>Я так не считаю
S>>Но даже если так, дети — это данные объекта. При рекурсивном обходе дерева детей используются какие либо внешние данные? Очевидно нет. И это веский повод инкапсулировать этот метод в данный класс. CRA>Очень интересная тем, может ли сам алгоритм обхода быть внешними данным, а считаю что ДА, так как таких алгоритмов может быть нескально, например: обход начиная с родителя, либо обход начиная с самого нижнего уровня. CRA>Из этого следует что, может существовать функциональность,которая требует различные алгоритмы обхода, следовательно, как написано Вами ниже необходимо создавать strategy или реализовывать IoC.
S>>А вот другой пример. Во многих бизнес приложениях есть функциональность, позволяющая настраивать правила нумерации экземпляров сущностей (заказов, накладных и т.п.). Вот это уже повод вынести данную функциональность за пределы сущности. Правда тут понадобится уже не хелпер а что-то вроде strategy или какой нибудь другой IoC. CRA>Выше я рассмотрел пример с алгоритмом обходом дерева (который, является кандидатом на инкапсуляцию) и почему то, я не вижу смысла инкапсулировать в сам объет несколько алгоритмов обхода. И по этому получается придется выносить данный алгоритм в другой класс, а не инкапсулировать. А вот как называть новый класс TreeBypassStategy реализуя стратегю, либо ObjectHelper не реализую оную. Да хоть ObjectManagerService. Главное, то, что алгоритм отделе от Object.
А смысл клиенты выбирать стратегию, по которой проходить дерево?
Не должен ли объект сам знать как он устроен и выбирать свои данные, изходя из этого знания, оптимальным образом.
Может там дерево это вообще в реляционном виде в базе хранится — клиенту эта информация, вообще говоря, ни к чему.
Здравствуйте, <Аноним>, Вы писали:
А>Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво...
Что именно выглядит некрасиво? И как красиво?
А>Кто как поступает с такими методами?
Делаю хелперы, методы делаю extension, что позволяет привязать их в интеллисенсе к объекту.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, <Аноним>, Вы писали:
А>Есть класс, который реализует некую ф-ть А>class Object А>У него есть методы, типа А>GetName — имя А>GetSize — возвращает свой размер без учета детей А>GetChilds — возвращает список своих детей
А>Появилась необходимость написать ряд методов, типа расчета размера рекурсивно с учетом всех детей, генерации уникального имени среди детей и т.д А>Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется. Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво... А>Кто как поступает с такими методами?
можно рассмотреть на обсосанном string
int string.toInt()
int toInt(string)
1. — 2.
Выход простой , складывать их в классы хелперы с нормальными названиями , наприммер ObjectTraversal, ObjectParamExtraxtor, и т.п.
3. Есть еще и третий выход , чтобы класс возвращал гетерами сервисные классы. например:
Object.getIteratorBFS и т.п. Которые реализованны с помощью публичного интерфейса класса но предоставляют функциональность в сторонке от класса.
iterator string.begin()
4. Или как четвыртый вариант , жесткое разделение на классы а потом в прикладном коде использование лекгих фасадов в зависимости от варианта использования.
MyCoolString : public string
{
Int toInt()
{
Helper1::toInt(this);
}
}
5. Не всегда Object вообще дожен появляться в пользовательском коде. Т.е. все детали могут быть скрыты за безопасными с точки зрения инкапсуляции фасадами.
MyCoolString
{
stringDataStorage
stringTraits1
stringTraits2
}
Теперь о самой идее складывания функций хелперов в отдельные классы.
Этот вопрос в програмировании обсуждается на протяжении десятилетий и до сих пор нет однозначного ответа (вспомните кормление вомбата).
Плюсы разделения:
— Улучшается инкапсуляция, набор интерфейсов к классу минимален и намного сложнее нарушить внутрение инварианты класса.
— Сущность наделяется меньшей ответственостью что улучшает общий дизайн, легче отделить ответственности класса.
— Избегаем широких интерфейсов.
Минусы разделения:
— Распыление функциональности на много классов, тяжелее в использовании.
— Есть опасность что при изменении Object , придется выносить наружу новые методы для реализации Helpers. Т.е. связь которая казалось совершенно внешней окажется очень интимной в будущем для объекта.
А решать как поступатьв дальнейшем только вам , к сожалению нет четкого ответа на этот вопрос , но последнее время все больше рекомендаций выносить в отдельные классы функции которые можно реализовать через публичный интерфейс.
Здравствуйте, Аноним, Вы писали:
А>Кто как поступает с такими методами?
Имхо всё это заморочки, лично я не вижу проблем в обоих вариантах: можно оставить эти методы в классе, а можно вынести в отдельный класс с конкретным названием.
Здравствуйте, MozgC, Вы писали:
MC>Здравствуйте, Аноним, Вы писали:
А>>Кто как поступает с такими методами?
MC>Имхо всё это заморочки, лично я не вижу проблем в обоих вариантах: можно оставить эти методы в классе, а можно вынести в отдельный класс с конкретным названием.
Как говорил Жванецций "Но если вас не интересует результат". Дело не в том чтобы найти криминал , я попытаться сделать как можно лучше. Я например хочу писать как можно лучше, и монимаю стремление к перфекционизму.
Первая проблема в названии топика. Подход неправильный. То, что Вы не
сообразите, куда их запихнуть, не говорит о том, что они "внешние".
> Есть класс, который реализует некую ф-ть > class Object > У него есть методы, типа > GetName — имя > GetSize — возвращает свой размер без учета детей
> GetChilds — возвращает список своих детей
<- вторая проблема тут. Или дети являются вложенными объектами, или...
Вообще говоря, почему тут метод, а не свойство, совершенно непонятно.
Кстати, а что оно возвращает?
> как внешние ф-ции в отдельный фаил ObjectHelper в виде > CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень > красиво...
Ессно не красиво. Вы бы еще в ObjectHelper вынесли Count. По сути
CalcFullObjectSize и GetObjectUniqChildName являются методами (или
свойствами) коллекции "детей". Где-то так:
public class Object
{
public string Name
{
get
{
return _name;
}
}
public int Size
{
get
{
return ...;
}
}
public ObjectCollection Children
{
get
{
return ...;
}
}
}
public class ObjectCollection : Collection //ну или чего там ближе...
{
public int Size // CalcFullObjectSize
{
get
{
int res = 0;
foreach (Object obj in List)
res += obj.Size + obj.Children.Size;
return res;
}
}
public string GetUniqueName()//GetObjectUniqChildName
{
//...
}
}
> Кто как поступает с такими методами?
Да точно так же. Ложу в ObjectHelper, помечаю как Obsolete.
Студия варнингами сыплет. Пока не станет понятно где эти методы должны
находиться на самом деле. Когда пойму — переношу в нужный класс.
Posted via RSDN NNTP Server 2.1 beta
Всё, что нас не убивает, ещё горько об этом пожалеет.
Здравствуйте, minorlogic, Вы писали:
M>Как говорил Жванецций "Но если вас не интересует результат". Дело не в том чтобы найти криминал , я попытаться сделать как можно лучше. Я например хочу писать как можно лучше, и монимаю стремление к перфекционизму.
Меня интересует результат, и я всегда пытаюсь сделать как можно лучше, просто в данном конкретном вопросе я не вижу косяков в обоих вариантах.
Здравствуйте, stump, Вы писали: S>Я говорю здесь об инкапсуляции данных и кода, которой зачастую необоснованно пренебрегают. Увлечение "хелпер" классами хороший пример подобного пренебрежения. Действительно, бывает масса обстоятельств, которые затрудняют подобную инкапсуляцию, и тогда хэлпер представляется наиболее простым решением в лоб. Но в большинстве случаев самое простое решение не самое удачное. Тут говорили, что хэлпер может превратится в помойку, но чаще возникает другая проблема. По мере усложнения кода хэлпера, он начинает влиять на интерфейс класса которому он "помогает". Хэлперу нужно все больше и больше знать о внутреннем состоянии объекта которому он "помогает". S>Хелперы увеличивают связность и размазывают логику. В тоже время, часто для таких случаев IoС, например dependency ijection, более удачное решение, поскольку не нарушает инкапсуляцию и порождает меньшую свяность. S>Поэтому прежде создавать хэлпер надо (1) хорошенько подумать можно ли этот код оставить в самом классе, и (2) если оставить его там нельзя стоит прикинуть, нет ли более подходящего варианта для этого (observer, service locator и т.п.).
Полностью согласен, с этими утверждениями, но вернемся к конкретному примеру в начале раздела.
Я думаю для именно этого конкретного случая, применение IoC,Visitors etc, было бы наибольшим усложнением
Здравствуйте, VUspenskiy, Вы писали:
VU>А смысл клиенты выбирать стратегию, по которой проходить дерево? VU>Не должен ли объект сам знать как он устроен и выбирать свои данные, изходя из этого знания, оптимальным образом. VU>Может там дерево это вообще в реляционном виде в базе хранится — клиенту эта информация, вообще говоря, ни к чему.
Можно прочитать отличия State от Stategy: One difference is that the Strategy is visible to (and configurable by) the
Client, whereas the
fact that a class has State is not visible to the Client.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Полностью согласен, с этими утверждениями, но вернемся к конкретному примеру в начале раздела. CRA>Я думаю для именно этого конкретного случая, применение IoC,Visitors etc, было бы наибольшим усложнением
Какого конкретного случая? Того, о котором пишет топикстартер? Так там все методы должны лежать в самом классе Object, поскольку там нет ни одного разумного обоснования для введения дополнительных классов. Все остальные предположения были сделаны уже в ходе обсуждения.
Я вижу достаточно ограниченный круг задач, которые стоит решать с помощью классов хэлперов:
1. Поместить в хэлпер общий алгоритм для нескольких классов объектов когда у них нет общего родительского класса или изменить этот класс невозможно.
2. Поместить в хэлпер дополнительный алгоритм который нельзя помещать в класс наследник, поскольку это нарушает принцип LSP (расширяет контракт наследника по сравнению с контрактом базового класса).
Больше ничего в голову не приходит.
Здравствуйте, Aikin, Вы писали:
A>- Не плодится дополнительный объект с непонятной зоной ответственности (об этом говорит имя объекта) // имхо, самый главный плюс
Для тех кому больше нравится инфиксная форма записи есть ExtensionMethod-ы.
A>- Сам класс может более эффективно решить эти задачи, так как знает свою внутреннюю структуру
Если ты пользуешься внутренней структурой (приватными данными), значит это действительно внутренний класс, но в этом случае и обсуждаемой проблемы бы не было. А если не пользуешься, значит это не аргумент.
A>- Требуемый для подсчета публичный интерфейс может в будущем измениться // самый неглавный плюс
Это самый главный минус такого подхода. Если интерфейс поменялся, то если класс внешний — об этом тут же скажет компилятор, и ты спокойно поправишь и реализацию подсчета. А вот если все реализовано внутри, то компилятор этого не заметит и в лучшем случае ты получишь ошибку рантайма, а в худшем — неверный результат.
A>Минусы:
— Нарушается инкапсуляция. Доступ к потрохам класса получает реализация, которой это ни разу не нужно.
Здравствуйте, stump, Вы писали:
S>Я говорю здесь об инкапсуляции данных и кода, которой зачастую необоснованно пренебрегают.
Как раз помещение в класс реализации, которой там быть не должно — это и есть нарушение инкапсуляции.
S>Хелперы увеличивают связность и размазывают логику. В тоже время, часто для таких случаев IoС, например dependency ijection, более удачное решение, поскольку не нарушает инкапсуляцию и порождает меньшую свяность.
А что есть DI, как не хелпер?
S>Поэтому прежде создавать хэлпер надо (1) хорошенько подумать можно ли этот код оставить в самом классе,
Здесь как раз критерий очень простой. Если для реализации данного функционала достаточно только публичного контракта класса, значит этот функционал должен быть снаружи класса.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Я говорю здесь об инкапсуляции данных и кода, которой зачастую необоснованно пренебрегают. IB>Как раз помещение в класс реализации, которой там быть не должно — это и есть нарушение инкапсуляции.
Должно — недолжно, какие критерии?
S>>Хелперы увеличивают связность и размазывают логику. В тоже время, часто для таких случаев IoС, например dependency ijection, более удачное решение, поскольку не нарушает инкапсуляцию и порождает меньшую свяность. IB>А что есть DI, как не хелпер?
DI это хелпер, помещенный внутрь класса. Большая разница, согласись.
S>>Поэтому прежде создавать хэлпер надо (1) хорошенько подумать можно ли этот код оставить в самом классе, IB>Здесь как раз критерий очень простой. Если для реализации данного функционала достаточно только публичного контракта класса, значит этот функционал должен быть снаружи класса.
Голословно. Обоснуй, пожалуйста. Ты утверждаешь, что функция, которая не использует ничего кроме публичного контракта класса должна быть вынесена в другой класс. Если придерживаться такого правила в классе останутся только геттеры, сеттеры. Какая от этого польза с точки зрения дизайна кода? Почему лучше держать такую функцию в классе хелпере? Почему связка класс + класс хелпер лучше чем просто класс, каких проблем это позволяет избежать?
Приведи пожалуйста ссылки и примеры, кто еще, кроме тебя рекомендует так поступать.
Здравствуйте, Aikin, Вы писали:
A>Это ваше мнение.
На оригинальность я здесь не претендую..
A>Вы имеете полное право его иметь и высказывать.
Ну, пасибо, что разрешил..
Здравствуйте, stump, Вы писали:
S>Должно — недолжно, какие критерии?
Здесь как раз критерий очень простой. Если для реализации данного функционала достаточно только публичного контракта класса, значит этот функционал должен быть снаружи класса.
(С)
S>DI это хелпер, помещенный внутрь класса.
Каким боком??! Оно как раз все снаружи и пользуется задекларированным публичным контрактом, в чем собственно и прелесть.
S>Ты утверждаешь, что функция, которая не использует ничего кроме публичного контракта класса должна быть вынесена в другой класс.
Именно.
S> Если придерживаться такого правила в классе останутся только геттеры, сеттеры.
Нет. А даже если и "да", то в чем, собственно, проблема?
S> Какая от этого польза с точки зрения дизайна кода?
Меньше связность, больше инкапсуляция.
S> Почему лучше держать такую функцию в классе хелпере? Почему связка класс + класс хелпер лучше чем просто класс, каких проблем это позволяет избежать?
Вот ты говорил об инкапсуляции, мол, пренебрегают ей. Можно ли ввести некий критерий, который позволит вычислить, что класс А более инкапсулирован чем класс Б? Таким критерием может служить количество потенциально поломанного кода, при изменении внутренней реализации класса. Допустим у нас есть класс A и метод foo(), которому для работы достаточно только публичного контракта класса A. Если метод foo() является членом класса A, то при изменении реализации класса A метод foo() потенциально может сломаться, так как он имеет доступ к потрохам класса, при этом точно так же может сломаться класс A при изменении foo(). Если же метод находится снаружи, то он гарантировано защищен от любых изменений внутренней реализации класса A, равно как и класс A от внутренних изменений foo(). Как следствие, инкапсуляция выше в том случае, когда метод foo() вынесен во внешний класс.
Теперь к разговору о DI. Давай теперь научим наш метод foo() (Ну или "хелперный класс", где foo живет) работать не с классом А, а с интерфейсом IA — вот тебе и готовый DI.
S>Приведи пожалуйста ссылки и примеры, кто еще, кроме тебя рекомендует так поступать.
Меерс, Саттер, МакКонел Александреску....
I've been battling programmers who've taken to heart the lesson that being object-oriented means putting functions inside the classes containing the data on which the functions operate. After all, they tell me, that's what encapsulation is all about.
They are mistaken.
(с) S. Meyers
Отдельная прелесть озвученного мной критерия заключается в том, что он очень хорошо поддается формализации. В отличии от абстрактных рассуждений из серии "вот эта логика вроде бы относится к классу, значит ее наверное надо внутрь, а вот эта кажется не относится, тогда, наверное, снаружи...", данный критерий очень четкий, конкретный.
Здравствуйте, IB, Вы писали:
IB>Допустим у нас есть класс A и метод foo(), которому для работы достаточно только публичного контракта класса A. Если метод foo() является членом класса A, то при изменении реализации класса A метод foo() потенциально может сломаться, так как он имеет доступ к потрохам класса, при этом точно так же может сломаться класс A при изменении foo(). Если же метод находится снаружи, то он гарантировано защищен от любых изменений внутренней реализации класса A, равно как и класс A от внутренних изменений foo(). Как следствие, инкапсуляция выше в том случае, когда метод foo() вынесен во внешний класс.
По-моему вы сами себе противоречите. Сначала вы говорите что "методу foo() для работы достаточно только публичного контракта класса А", а потом "при изменении реализации класса А метод foo() потенциально может сломаться". Это как он может потенциально сломаться если он использует только публичный контракт класса А? В этом случае я не вижу разницы между тем где находится метод foo(). Объясните пожалуйста.
Здравствуйте, MozgC, Вы писали:
MC> Это как он может потенциально сломаться если он использует только публичный контракт класса А?
Очень просто. В одном случае все целиком лежит на хрупких плечах разработчика и зачастую не одного, а во втором — контролируется компилятором.
Здравствуйте, Аноним, Вы писали:
А>Появилась необходимость написать ряд методов, типа расчета размера рекурсивно с учетом всех детей, генерации уникального имени среди детей и т.д А>Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется. Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво... А>Кто как поступает с такими методами?
Мне название Helper в имени класса не нравится, т.к оно ничего не говорит о том, какую помощь этот помошник оказывает. Я обычно разделяю таких помошников на мелкие классы по зоне ответственности, например:
ObjectSizeCalculator, у которого есть метод типа calc( Object ).
Преимущество таких классов в том, что их можно настраивать. Например устанавливать рекурсивный подсчет или выкидывать по определенному условию некоторые объекты из этого подсчета.
IB пишет: > Вот ты говорил об инкапсуляции, мол, пренебрегают ей. Можно ли ввести > некий критерий, который позволит вычислить, что класс А более > инкапсулирован чем класс Б?
Прелесть. Манагеры LOC считали, а IB теперь считает степень инкапсуляции
классов.
> Отдельная прелесть озвученного мной критерия заключается в том, что он > очень хорошо поддается формализации.
Ты лучше объясни мне, нафига оно надо? Ну, посчитали, и чего с ним
делать-то? LOC тоже очень хорошо формализировался. Вот только толку от
него было и есть ноль.
Posted via RSDN NNTP Server 2.1 beta
Всё, что нас не убивает, ещё горько об этом пожалеет.
Здравствуйте, Ромашка, Вы писали:
Р>Ты лучше объясни мне, нафига оно надо? Ну, посчитали, и чего с ним Р>делать-то? LOC тоже очень хорошо формализировался. Вот только толку от Р>него было и есть ноль.
Это в основном программеры уверены, что пользы от него ноль. А те, кто использует, придерживается иного мнения.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Должно — недолжно, какие критерии? IB>
IB>Здесь как раз критерий очень простой. Если для реализации данного функционала достаточно только публичного контракта класса, значит этот функционал должен быть снаружи класса.
IB>(С)
S>>DI это хелпер, помещенный внутрь класса. IB>Каким боком??! Оно как раз все снаружи и пользуется задекларированным публичным контрактом, в чем собственно и прелесть.
S>>Ты утверждаешь, что функция, которая не использует ничего кроме публичного контракта класса должна быть вынесена в другой класс. IB>Именно.
S>> Если придерживаться такого правила в классе останутся только геттеры, сеттеры. IB>Нет. А даже если и "да", то в чем, собственно, проблема?
S>> Какая от этого польза с точки зрения дизайна кода? IB>Меньше связность, больше инкапсуляция.
Один класс — ноль связей. Два класса — одна связь. Один > ноль.
S>> Почему лучше держать такую функцию в классе хелпере? Почему связка класс + класс хелпер лучше чем просто класс, каких проблем это позволяет избежать? IB>Вот ты говорил об инкапсуляции, мол, пренебрегают ей. Можно ли ввести некий критерий, который позволит вычислить, что класс А более инкапсулирован чем класс Б? Таким критерием может служить количество потенциально поломанного кода, при изменении внутренней реализации класса. Допустим у нас есть класс A и метод foo(), которому для работы достаточно только публичного контракта класса A. Если метод foo() является членом класса A, то при изменении реализации класса A метод foo() потенциально может сломаться, так как он имеет доступ к потрохам класса, при этом точно так же может сломаться класс A при изменении foo(). Если же метод находится снаружи, то он гарантировано защищен от любых изменений внутренней реализации класса A, равно как и класс A от внутренних изменений foo(). Как следствие, инкапсуляция выше в том случае, когда метод foo() вынесен во внешний класс.
Тв путаешь инкапсуляцию и изоляцию. Нет никакой нужды защищать метод foo() от изменений в классе, когда этот метод принадлежит самому классу.
S>>Приведи пожалуйста ссылки и примеры, кто еще, кроме тебя рекомендует так поступать. IB>Меерс, Саттер, МакКонел Александреску.... IB>
IB>I've been battling programmers who've taken to heart the lesson that being object-oriented means putting functions inside the classes containing the data on which the functions operate. After all, they tell me, that's what encapsulation is all about.
IB>They are mistaken.
IB>(с) S. Meyers
Очень убедительно.... "Они ошибались", почему?
IB>Отдельная прелесть озвученного мной критерия заключается в том, что он очень хорошо поддается формализации. В отличии от абстрактных рассуждений из серии "вот эта логика вроде бы относится к классу, значит ее наверное надо внутрь, а вот эта кажется не относится, тогда, наверное, снаружи...", данный критерий очень четкий, конкретный.
Нет, критерии тут тоже четкие. Функция работает с состоянием класса значит это функция класса, плюс принцип single responsibility.
Критерии того когда функцию стоит выносить в хелпер я тут уже описывал.
Вамое главное что мои критерии причинные, а твои хоть и четкие но причины ты для них сформклировать не можешь.
Здравствуйте, stump, Вы писали:
IB>>Меньше связность, больше инкапсуляция. S>Один класс — ноль связей.
Как это ноль? Про связи внутри класса ты уже забыл? Зря, они как раз самые страшные.
S> Нет никакой нужды защищать метод foo() от изменений в классе, когда этот метод принадлежит самому классу.
Обосновать можешь?
S>Нет, критерии тут тоже четкие. Функция работает с состоянием класса значит это функция класса
Обосновать можешь?
S>, плюс принцип single responsibility.
SRP тут вообще не в тему.
S>Вамое главное что мои критерии причинные, а твои хоть и четкие но причины ты для них сформклировать не можешь.
Причину тебе уже неоднократно называли. Могу повторить — как и у подавляющего большинства архитектурных правил, это уменьшение связности кода.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, stump, Вы писали:
S>Я, кстати, тоже во всю использую "анемичную модель". Но тут немного другой случай, ближе к грунту, так сказать.
По мне, использование Poor domain позволяет увеличить возможность повторного использования, как алгоритмов работы над доменом, так и самих доменных объектов.
Lloyd пишет: > Это в основном программеры уверены, что пользы от него ноль. А те, кто > использует, придерживается иного мнения.
А я и не программер нынче. Только толку от него как было ноль, так и
осталось. Я бы даже сказал, минус. Иногда смотришь на код работнечгов и
думаешь: нафига нужно было вот это размазывание манной каши по тарелке...
Posted via RSDN NNTP Server 2.1 beta
Всё, что нас не убивает, ещё горько об этом пожалеет.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, stump, Вы писали:
IB>>>Меньше связность, больше инкапсуляция. S>>Один класс — ноль связей.
AVK>Как это ноль? Про связи внутри класса ты уже забыл? Зря, они как раз самые страшные.
В software design под связностью (coupling) понимают связи между структурными единицами (классами, модулями).
Классы как раз и создаются для того чтобы инкапсулировать семантически родственные связи. Чтобы они не были очень страшными, сле дует придерживаться определенный принципов проектирования (SRP к примеру).
S>> Нет никакой нужды защищать метод foo() от изменений в классе, когда этот метод принадлежит самому классу.
AVK>Обосновать можешь?
Могу. Принцип черного ящика, метод инкапсулирован в класс, не имеет значения, что и как происходит внутри, важен только интерфейс.
S>>Нет, критерии тут тоже четкие. Функция работает с состоянием класса значит это функция класса
AVK>Обосновать можешь?
Собственно целый день этим и занимаюсь. Имеющий глаза увидит.
S>>Вамое главное что мои критерии причинные, а твои хоть и четкие но причины ты для них сформклировать не можешь.
AVK>Причину тебе уже неоднократно называли. Могу повторить — как и у подавляющего большинства архитектурных правил, это уменьшение связности кода.
Вместо одного класса два. Уменьшили, блин.
Здравствуйте, stump, Вы писали:
S>В software design под связностью (coupling) понимают связи между структурными единицами (классами, модулями).
Хочешь поспорить о терминах? Ну пусть это будет не coupling, назови как хочешь. Главное — связи внутри класса еще страшнее связей между классами.
S>Классы как раз и создаются для того чтобы инкапсулировать семантически родственные связи.
И что? Это как то снимает проблему меньшей контроллируемости связей внутри класса и нелинейного роста сложности при увеличении числа связей?
S> Чтобы они не были очень страшными, сле дует придерживаться определенный принципов проектирования
Следует. Но чем больше в этом участвует компилятор, тем лучше.
S>>> Нет никакой нужды защищать метод foo() от изменений в классе, когда этот метод принадлежит самому классу.
AVK>>Обосновать можешь? S>Могу. Принцип черного ящика
Принцип черного ящика не применим, если мы говорим об внутреннем устройстве собственно ящика.
S>, метод инкапсулирован в класс
И что?
S>, не имеет значения, что и как происходит внутри, важен только интерфейс.
Это все понятно. Непонятно, почему, если метод пользуется только публичным контрактом обрабатываемого класса, то начинает иметь значение все, что происходит внутри этого метода? Хелпер точно так же выставляет наружу контракт, а детали реализации прячет внутри.
S>>>Нет, критерии тут тоже четкие. Функция работает с состоянием класса значит это функция класса
AVK>>Обосновать можешь? S>Собственно целый день этим и занимаюсь. Имеющий глаза увидит.
Поскольку глаза у меня есть, а увидеть не увидел, то твое утверждение неверно.
AVK>>Причину тебе уже неоднократно называли. Могу повторить — как и у подавляющего большинства архитектурных правил, это уменьшение связности кода. S>Вместо одного класса два. Уменьшили, блин.
Да, конечно. Связи между классами намного слабее и жестче контроллируются, нежели связи внутри одного класса. Тут есть о чем спорить?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
public class Order
{
public OrderLine Lines {get; set;}
public decimal getTotal()
{
decimal res = 0;
foreach(OrderLine line in lines)
res += line.getSubTotal();
return res;
}
}
public class OrderLine
{
public Product Product {get; set;}
public decimal Count {get; set;}
public decimal Price {get;set}
public decimal getSubTotal()
{
return Count * (Price + Product.TaxRate/100 * Price);
}
}
Методы getTotal и getSubTotal используют только публичный интерфейс классов. Значит я долженн вынести их в вот такой класс:
public class OrderCalcHelper
{
public decimal getSubTotal(OrderLine line)
{
return line.Count * (line.Price + line.Product.TaxRate/100 * line.Price);
}
publi decimal getTotal(Order order)
{
decimal res = 0;
foreach(OrderLine line in order.lines)
res += line.getSubTotal();
return res;
}
}
Теперь, если кто умеет, посчитайте метрики связности кода для случая с классом OrderCalcHelper и без него.
Рассмотрим кейс, когда мы используем класс Order и нам нужно получить сумму по заказу. Я вижу Order и мне нужна сумма, и ничто мне не говорит здесь, что где-то есть замечательный хелпер, который умеет считать нужную мне сумму (вероятно однажды замучившись искать подобные хелперы, кто-то в Microsoft придумал методы-расширения).
Рассмотрим кейс внесения изменения в контракт классов Order, OrderLine. Компилятор нам поможет в случае с OrderCalcHelper, но неужели компилятор становится бессильным в его отсутствии?
Напоследок небольшое наблюдение из практики. В проектах, использующих ADO.NET часто можно встретить различные SQL-хелперы. Они помогают конвертировать значения, работать с ридерами, с SQL параметрами и т.д. и т.п. Надо заметить, что наличие таких хелперов безусловно полезно. Но интересно другое. Если система достаточно велика, и (или) стара, так чтобы над ней работали более 4 программистов, практически гарантировано при code review можно найти парочку хелперов, которые делают одно и то-же. Это и есть основная проблема хэлперов, которую грубо называют размазывание логики.
Здравствуйте, stump, Вы писали:
S>Теперь, если кто умеет, посчитайте метрики связности кода для случая с классом OrderCalcHelper и без него.
Очень мне было интересно, прикинуть метрики. Вот результат расчета VS2008:
С Helper'ом Namespace: Test
Maintainability Index: 93
Cyclomatic Complexity: 19
Depth of Inheritance: 1
Class Coupling: 7
Lines of Code: 22
Без Helper'а Namespace: Test
Maintainability Index: 92
Cyclomatic Complexity: 18
Depth of Inheritance: 1
Class Coupling: 6
Lines of Code: 21
Получается по расчетам VS, сопровождаемость программы с Helper'ом лучше чем без него
AVK>>Как это ноль? Про связи внутри класса ты уже забыл? Зря, они как раз самые страшные. S>В software design под связностью (coupling) понимают связи между структурными единицами (классами, модулями). S>Классы как раз и создаются для того чтобы инкапсулировать семантически родственные связи. Чтобы они не были очень страшными, сле дует придерживаться определенный принципов проектирования (SRP к примеру).
Следуя твоей логике наименьшая связанность будет у программы с main и одним классом/функцией. Очевидный для меня абсурд.
Здравствуйте, stump, Вы писали:
S>Один класс — ноль связей. Два класса — одна связь. Один > ноль.
Дай я продолжу твою мысль.. Если мы вообще весь код поместим в один класс, то у нас получится идеальная программа с точки зрения связности.. Гениально! =)
Проблема связности не в публичных связях между классами, а в неявных связях как внутри одной сущности, так и между ними, на что тебе собственно Андрей уже намекал.
К слову, классический DI, если следовать твоей логике, увеличивает связность минимум в трое..
S>Тв путаешь инкапсуляцию и изоляцию.
Изоляция — это вообще термин из области CAS и контроля памяти, так что если тут кто что и путает, то точно не я..
Я описал почти классический пример, для чего в принципе нужна инкапсуляция.
S> Нет никакой нужды защищать метод foo() от изменений в классе, когда этот метод принадлежит самому классу.
То есть, тот факт, что метод физически впихнули внутрь класса, автоматически делает его реализацию корректной?
Я уже приводил пример к чему может привести подобный подход...
S>Очень убедительно.... "Они ошибались", почему?
Ну, я же не буду тебе всего Меерса цитировать.. ) Почитай, там много полезного, он довольно подробно все это разжевывает..
S>Нет, критерии тут тоже четкие. Функция работает с состоянием класса значит это функция класса,
Отличный критерий! С состоянием класса, так или иначе, работает все, что работает с этим классом.
Сериализатор работает с состоянием? Конечно, пихаем в класс. Сохранятор в БД работает? Безусловно, кладем. UI работает? Ну, да, а что же он показывает, как не состояние? Конечно засовываем в класс! =))
Мы опять приходим к одному большему классу на всю программу, в последовательности тебе не откажешь..
S> плюс принцип single responsibility.
Причем он тут?
S>Критерии того когда функцию стоит выносить в хелпер я тут уже описывал.
В хелпер класс можно выносить аспекты, не свойственные для самого класса
Ты вот это называешь четким критерием? Прости, но четкости, равно как и причинности, я тут что-то не улавливаю. Какие аспекты свойственны для класса, а какие нет?
S>Вамое главное что мои критерии причинные,
Так в чем причина? Вот лично я, убей байт, не вижу причину по которой "код форматирования данных класса для сериализации" является не свойственным аспектом для класса, а "рассчет размера" — свойственным.
S> а твои хоть и четкие но причины ты для них сформклировать не можешь.
Могу еще раз напомнить...
В Software Design есть такой термин, как связность (coupling), который определяет на самом деле вовсе не количество связей между структурными единицами (классами), так связность только студия трактует, когда метрики считает, а количество кода, которое ты можешь сломать внеся изменение в одну строчку. Определение, конечно не формальное, зато доходчивое. Формальное же определение связности пляшет не просто от количества связей между модулями, а от количества неконтролируемых связей: Low coupling refers to a relationship in which one module interacts with another module through a stable interface and does not need to be concerned with the other module's internal implementation.
Как видишь, с контролируемыми связями все в порядке, проблема с неконтролируемыми.
Чтобы связность как-то контролировать, придумали такую штуку, как инкапсуляция. Можно выделить публичный контракт и любое внутреннее изменение, гарантировано не влияет на то, что находится за пределами контракта. Таким образом неконтролируемая связь между строчками кода, частично трансформируется в контролируемую компилятором связь между сущностями, оборудованными публичным контрактом. Следовательно, внеся foo() в A — ты теряешь контроль над связью, что приводит к увеличиению связности, вынеся же foo() за пределы A — ты обретаешь контроль и уменьшаешь связность.
Иными словами, у тебя есть два модуля — A и foo(). Когда A и foo() торчат в пределах одного класса, то ты не как не можешь быть уверенным — foo() concerned with the A internal implementation или еще нет. А вот если они разнесены по разным классам, то они гарантировано interacts through a stable interface, что способствует низкой связности.
Я достаточно подробно сформулировал причину?
Здравствуйте, stump, Вы писали:
S>Здравствуйте, IB, Вы писали:
S>Вот вам пример. Заказ: S>
S>public class Order
S>{
S> public OrderLine Lines {get; set;}
S> public decimal getTotal()
S> {
S> decimal res = 0;
S> foreach(OrderLine line in lines)
S> res += line.getSubTotal();
S> return res;
S> }
S>}
S>public class OrderLine
S>{
S> public Product Product {get; set;}
S> public decimal Count {get; set;}
S> public decimal Price {get;set}
S> public decimal getSubTotal()
S> {
S> return Count * (Price + Product.TaxRate/100 * Price);
S> }
S>}
S>
S>Методы getTotal и getSubTotal используют только публичный интерфейс классов. Значит я долженн вынести их в вот такой класс: S>
S>public class OrderCalcHelper
S>{
S> public decimal getSubTotal(OrderLine line)
S> {
S> return line.Count * (line.Price + line.Product.TaxRate/100 * line.Price);
S> }
S> public decimal getTotal(Order order)
S> {
S> decimal res = 0;
S> foreach(OrderLine line in order.lines)
S> res += line.getSubTotal();
S> return res;
S> }
S>}
S>
А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше?
Здравствуйте, stump, Вы писали:
S>Теперь, если кто умеет, посчитайте метрики связности кода для случая с классом OrderCalcHelper и без него.
Всего у нас четыре модуля — два класса и два метода. Следуя формальному определению связности (Low coupling refers to a relationship in which one module interacts with another module through a stable interface and does not need to be concerned with the other module's internal implementation), получаем:
В первом случае у нас две неявных связи и ни одной посредством публичного контракта, то есть, методы concerned with the other module's internal implementation — ни о какой низкой связности речь не идет. Во втором, две связи посредством публичного контракта, то есть one module interacts with another module through a stable interface, и ни одной неявной.
Счет, очевидно, два-ноль в пользу выноса обсуждаемых функций в хелперный класс, ЧТД. =))
S>Я вижу Order и мне нужна сумма, и ничто мне не говорит здесь, что где-то есть замечательный хелпер, который умеет считать нужную мне сумму (вероятно однажды замучившись искать подобные хелперы, кто-то в Microsoft придумал методы-расширения).
Во-первых тебе говорят методы расширения, а во-вторых, тебе говорит знание имеющихся в наличии сервисов. Подобная архитектура очень быстро приучает изучить доступный инструментарий, а не изобретать что-то свое.
S>Рассмотрим кейс внесения изменения в контракт классов Order, OrderLine. Компилятор нам поможет в случае с OrderCalcHelper, но неужели компилятор становится бессильным в его отсутствии?
Угу, становится, особенно, когда у класса есть приватное состояние, жизненный цикл больше недели и правят его код разные разработчики.
Могу еще один кейс привести.
Ты знаком с той концепцией, что при добавлении нового функционала надо не модифицировать имеющийся код, а дописывать новый? Допустим, теперь появился альтернативный способ рассчета Total и Subtotal, ну или какой-нибудь еще агрегат по заказам, позарез нужный аналитикам (а зная аналитиков, таких будет даже не один и не два). Ты будешь вынужден модифицировать существующие классы, я же просто допишу новый внешний сервис.
Это все к вопросу о связности и твоем любимом SRP — держать данные заказа, это одна ответственность, а заниматься аналитикой по заказам — это уже ответственность совершенно другая, таким образом, мы должны завести хелперный класс, хотя бы следуя SRP.. =)
S>Напоследок небольшое наблюдение из практики.
<...> S> Это и есть основная проблема хэлперов, которую грубо называют размазывание логики.
Могу поделиться своим. На моей практике, на таких проектах хелперов не остается вообще. Жизненный цикл типичного метода выглядит примерно так: Сначала он выносится в хелпер, потом хелпер обрастает еще парой-тройкой методов, потом этот хелпер трансформируется в обычный сервис реализующий функционал типичный для данного семейства объектов, и подключаемый посредством DI или какого-либо еще IoC. И большая часть кода состоит из таких вот сервисов — очень удобно.. )
Здравствуйте, IB, Вы писали:
IB>Это все к вопросу о связности и твоем любимом SRP — держать данные заказа, это одна ответственность, а заниматься аналитикой по заказам — это уже ответственность совершенно другая, таким образом, мы должны завести хелперный класс, хотя бы следуя SRP.. =)
А почему вы считаете что ответственность класса Order это только держать данные для заказа? Так можно начать про что угодно говорить. Давайте скажем что ответственность класса String — держать набор символов, а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength(), StringService.IndexOf(), StringService.StringToUpper() и т.д.
MC>А почему вы считаете что ответственность класса Order это только держать данные для заказа? Так можно начать про что угодно говорить. Давайте скажем что ответственность класса String — держать набор символов, а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength(), StringService.IndexOf(), StringService.StringToUpper() и т.д.
Ну дык на протяжении 5 лет , кто тольок это не предлагал
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, stump, Вы писали:
AVK>>>Как это ноль? Про связи внутри класса ты уже забыл? Зря, они как раз самые страшные. S>>В software design под связностью (coupling) понимают связи между структурными единицами (классами, модулями). S>>Классы как раз и создаются для того чтобы инкапсулировать семантически родственные связи. Чтобы они не были очень страшными, сле дует придерживаться определенный принципов проектирования (SRP к примеру).
M>Следуя твоей логике наименьшая связанность будет у программы с main и одним классом/функцией. Очевидный для меня абсурд.
Этот абсурд ты только что сам придумал. У меня про это ни слова не сказано, вероятно ты ни слова не понял из того, что процитировал выше. Там как раз говорится о том что функциональность делится по разными классам, и чем при этом руководствуются.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Один класс — ноль связей. Два класса — одна связь. Один > ноль. IB>Дай я продолжу твою мысль.. Если мы вообще весь код поместим в один класс, то у нас получится идеальная программа с точки зрения связности.. Гениально! =)
Давай ты не будешь передергивать и за меня делать выводы. IB>Проблема связности не в публичных связях между классами, а в неявных связях как внутри одной сущности, так и между ними, на что тебе собственно Андрей уже намекал.
Я не понимаю что такое неявные связи внутри сущности. Если в классе "строка" свойство "длинна" зависит от свойства "символы" то это неявная связь? Что в ней плохого? От нее надо избавляться?
То что ты, и Андрей называете внутренними неявными связями называется cohession (на русский обычно переводится как "зацепление"). Действительно связность и зацепление находятся в некотором противоречии друг с другом. Большое зацеление так же плохо как и большая связность, но с ним борются другими методами.
IB>К слову, классический DI, если следовать твоей логике, увеличивает связность минимум в трое..
Да DI увеличивает связность и это оправдано, поскольку он решает ряд других проблем.
S>>Тв путаешь инкапсуляцию и изоляцию. IB>Изоляция — это вообще термин из области CAS и контроля памяти, так что если тут кто что и путает, то точно не я.. IB>Я описал почти классический пример, для чего в принципе нужна инкапсуляция.
S>> Нет никакой нужды защищать метод foo() от изменений в классе, когда этот метод принадлежит самому классу. IB>То есть, тот факт, что метод физически впихнули внутрь класса, автоматически делает его реализацию корректной? IB>Я уже приводил пример к чему может привести подобный подход...
Компилятору без разницы где лежит метод, внутри класса или вне класса. S>>Очень убедительно.... "Они ошибались", почему?
IB>Ну, я же не буду тебе всего Меерса цитировать.. ) Почитай, там много полезного, он довольно подробно все это разжевывает..
Надо приводить аргументы, а не "авторитетное мнение". А то как в анекдоте получается "- Василий Иваныч, а ты за какой интернационал? — А Ленин за какой?, вот и я за этот!"
S>>Нет, критерии тут тоже четкие. Функция работает с состоянием класса значит это функция класса, IB>Отличный критерий! С состоянием класса, так или иначе, работает все, что работает с этим классом. IB>Сериализатор работает с состоянием? Конечно, пихаем в класс. Сохранятор в БД работает? Безусловно, кладем. UI работает? Ну, да, а что же он показывает, как не состояние? Конечно засовываем в класс! =)) IB>Мы опять приходим к одному большему классу на всю программу, в последовательности тебе не откажешь..
S>> плюс принцип single responsibility. IB>Причем он тут?
Да при том, что сериализация, сохранение в БД, UI не относятся к responsibility того класса, который мы обсуждаем. В последовательности мне не откажешь
S>>Критерии того когда функцию стоит выносить в хелпер я тут уже описывал. IB>
IB>В хелпер класс можно выносить аспекты, не свойственные для самого класса
IB>Ты вот это называешь четким критерием? Прости, но четкости, равно как и причинности, я тут что-то не улавливаю. Какие аспекты свойственны для класса, а какие нет?
Плохо, что не улавливаешь. Почитай про GRASP паттерны проектирования. Но вообще-то для этого нужна практика, и критический подход ко всему что делаешь.
S>>Вамое главное что мои критерии причинные, IB>Так в чем причина? Вот лично я, убей байт, не вижу причину по которой "код форматирования данных класса для сериализации" является не свойственным аспектом для класса, а "рассчет размера" — свойственным.
Тут все очевидно, класс создавался для представления каких-то там данных, класс агрегирует в себе какое-то количество данных, класс может предоставить любую часть этих данных для чтения (записи) другим классам, класс сообщает каков размер содержащихся в нем данных. Все это "свойственно" для данного класса. Это SRP. Форматирование для сериализации — это не свойственно для класса, поскольку это другой тип ответственности (один у него уже есть, другой надо выносить во внешний класс).
S>> а твои хоть и четкие но причины ты для них сформклировать не можешь. IB>Могу еще раз напомнить... IB>В Software Design есть такой термин, как связность (coupling), который определяет на самом деле вовсе не количество связей между структурными единицами (классами), так связность только студия трактует, когда метрики считает, а количество кода, которое ты можешь сломать внеся изменение в одну строчку. Определение, конечно не формальное, зато доходчивое. Формальное же определение связности пляшет не просто от количества связей между модулями, а от количества неконтролируемых связей: Low coupling refers to a relationship in which one module interacts with another module through a stable interface and does not need to be concerned with the other module's internal implementation. IB>Как видишь, с контролируемыми связями все в порядке, проблема с неконтролируемыми. IB>Чтобы связность как-то контролировать, придумали такую штуку, как инкапсуляция. Можно выделить публичный контракт и любое внутреннее изменение, гарантировано не влияет на то, что находится за пределами контракта. Таким образом неконтролируемая связь между строчками кода, частично трансформируется в контролируемую компилятором связь между сущностями, оборудованными публичным контрактом. Следовательно, внеся foo() в A — ты теряешь контроль над связью, что приводит к увеличиению связности, вынеся же foo() за пределы A — ты обретаешь контроль и уменьшаешь связность. IB>Иными словами, у тебя есть два модуля — A и foo(). Когда A и foo() торчат в пределах одного класса, то ты не как не можешь быть уверенным — foo() concerned with the A internal implementation или еще нет. А вот если они разнесены по разным классам, то они гарантировано interacts through a stable interface, что способствует низкой связности. IB>Я достаточно подробно сформулировал причину?
Спасибо,я давно понял твою аргументацию, но не могу с ней согласиться
Дело в том что если foo() является функцией (в математическом смысле) исключительно только данных класса, то не зависимо от того, где ты ее разместишь, внутри самого класса, или вне, степень зависимости foo() от данных класса не изменяется. Это очевидно. Зато когда foo() находится внутри класса она входит в интерфейс самого класса и как ты говоришь, "любое внутреннее изменение, гарантировано не влияет на то, что находится за пределами контракта". То есть у нас все инкапсулировано. Мы можем менять и внутренне устройство данных класса и реализацию foo() и никакие другие классы это не затронет. Если foo() лежит в другом классе, то появляется связь. Самое печальное в том, что теперь не только интерфейс класса А влияет на функцию B.foo(), но и функция B.foo() влияет на класс А. Если нам понадобиться изменить алгоритм foo() то с большОй долей вероятности это выльется в изменение интерфейса класса A. Если вспомнить о том, что foo() использует только данные класса A, то подобные изменения его интерфейса, это довольно неприятный побочный эффект, которого можно избежать, инкапсулировав foo() в A.
Надеюсь, я достаточно подробно сформулировал свою точку зрения.
Здравствуйте, Ага, Вы писали:
Ага>А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше?
Нет, давай останемся в рамках первоначальных условий. Все мы понимаем, что изменение требований влечет изменение дизайна.
Здравствуйте, MozgC, Вы писали:
IB>>Это все к вопросу о связности и твоем любимом SRP — держать данные заказа, это одна ответственность, а заниматься аналитикой по заказам — это уже ответственность совершенно другая, таким образом, мы должны завести хелперный класс, хотя бы следуя SRP.. =) MC>А почему вы считаете что ответственность класса Order это только держать данные для заказа? Так можно начать про что угодно говорить. Давайте скажем что ответственность класса String — держать набор символов, а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength(), StringService.IndexOf(), StringService.StringToUpper() и т.д.
Если бы функциональность класса String менялась каждую неделю, то так бы и сделали.
Ага>А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше?
Не аргумент. Они потребуют практически одинаковой работы по переделке дизайна. TaxRate станет внешним параметром.
Я с интересом слежу за ходом дискуссии, т.к. применяю оба подхода и вижу преимущества и в том и другом случае. Пока выбор той или иной стратегии идет с учетом интуиции. Более формальных правил я придумать не смог.
Аргумент про использование только публичного контракта плохо ложится на использование доменной модели, там все данные являются контрактом. Если взять в качестве закона:
Если для реализации данного функционала достаточно только публичного контракта класса, значит этот функционал должен быть снаружи класса.
Значит единственным правильным решением является anemic domain model. POCO/POJO могут быть хороши в одних ситуациях, но при использовании такой модели меня не покидает мысль о том, что работа с ними ничем не отличается от использования DataSet'ов.
Здравствуйте, 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>Да и с самого начала сделали, если бы вовремя задумались... Собственно, на этот косяк им только ленивый не указывал.
А в чем косяк? Только без абстрактной теории, пожалуйста, на практике в чем косяк?
IB и AndrewVK, по-моему вы противоречите друг другу.
IB говорит что упоминаемые функции String надо вынести в отдельные классы,
а AndrewVK, говорит что нельзя — "перфоманс уже не тот".
Здравствуйте, MozgC, Вы писали:
MC>Зачем?
чтобы уменьшить связность и упростить публичный контракт.
MC>А в чем косяк?
Высокая связность.
MC>Только без абстрактной теории, пожалуйста, на практике в чем косяк? Re[2]: Куда девать ф-ции внешние для класса
Здравствуйте, MozgC, Вы писали:
MC>IB и AndrewVK, по-моему вы противоречите друг другу.
Ничуть.
MC>IB говорит что упоминаемые функции String надо вынести в отдельные классы,
Я оптом ответил, а Андрюша подробно разобрал, какие нужно выносить, а какие нет.
MC>а AndrewVK, говорит что нельзя — "перфоманс уже не тот".
AVK говорит, что данный метод для своей корректной работы (например из соображений производительности), должен иметь доступ к приватным переменным класса => этот метод имеет полное право быть членом класса.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, MozgC, Вы писали:
MC>>Зачем? IB>чтобы уменьшить связность и упростить публичный контракт.
MC>>А в чем косяк? IB>Высокая связность.
MC>>Только без абстрактной теории, пожалуйста, на практике в чем косяк? IB>Re[2]: Куда девать ф-ции внешние для класса
На примере, пожалуйста, какие проблемы вам доставляет то, что все эти функции находятся в классе String? Мне вот это никогда проблем не доставляло, а наоборот удобно, но я возможно ерундой занимаюсь, а вы возможно встречали причины когда инкапсуляция этих функций в классе String доставляла вам проблемы, поделитесь парой примеров?
Здравствуйте, MozgC, Вы писали:
MC>На примере, пожалуйста,
Там была ссылка на пример.
MC> какие проблемы вам доставляет то, что все эти функции находятся в классе String?
Проблемы доставляет не мне, а тем, кто String разрабатывает и поддерживает.
MC> но я возможно ерундой занимаюсь,
Возможно..
Здравствуйте, AndrewVK, Вы писали:
AVK>Ну вот, ты и сам все знаешь
Судя по согласию IB в данной шутке только доля шутки.
Какие рекомендации будут по замене ORM? Полный отказ, поиск, покупка и освоение ORM который сможет работать с правильным контрактом или написание своего?
Я пока в таких вопросах решаю ситуацию в пользу имеющегося и освоенного инструмента, небольшая кривость в дизайне меньше повредит проекту чем все перечисленные решения.
Может я не вижу очевидного решения которое не затормозит разработку на несколько человекомесяцев и будет удовлетворять всем канонам дизайна?
Здравствуйте, IB, Вы писали:
MC>>На примере, пожалуйста, IB>Там была ссылка на пример.
Я попросил показать реальный пример про String раз вы утверждаете что он реализован неправильно и эти функции надо было вынести в отдельные классы.
MC>> но я возможно ерундой занимаюсь, IB>Возможно..
Так и думал что вы так напишите. Как достойно с вашей стороны было прокомментировать это мое замечание.
Здравствуйте, IB, Вы писали:
MC>> какие проблемы вам доставляет то, что все эти функции находятся в классе String? IB>Проблемы доставляет не мне, а тем, кто String разрабатывает и поддерживает.
Между тем мне лично ими удобнее пользоваться. Может быть это и есть причина по которой МС пошли на удорожание поддержки?
Здравствуйте, AndrewVK, Вы писали:
AVK>Ну почему сразу замене? Можно и оставить. Тут главное — не выдавать косяки дизайна, вызванные кривостью ORM, за правильное архитектурное решение.
В том-то и дело, что я не уверен, что это косяк дизайна. Возможно вынесение метода в отдельный статик хелпер снизит стоимость поддержки доменной модели. Но оно же повысит стоимость кода который будет ей пользоваться.
Extension methods в принципе решают эту проблему и я использую их в подобных случаях. Но топик вроде бы не ограничен C# 3.0.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Давай ты не будешь передергивать и за меня делать выводы. IB>Я не передергиваю, я продолжаю твою мысль.
Давай каждый свои мысли будет продолжать
S>>Я не понимаю что такое неявные связи внутри сущности. IB>Ну посмотри в википедию: IB>
IB>Some types of coupling, in order of highest to lowest coupling, are as follows:
IB>Content coupling (high)
IB>Content coupling is when one module modifies or relies on the internal workings of another module (e.g. accessing local data of another module).
IB>Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module.
IB>...
IB>Это самый сильный (высокий) тип связности.
Так ведь то что ты предлагаешь (b.foo()) это и есть "accessing local data of another module", т.е. самый сильный тип связности. Что с того что он все эти данные тягает через паблик интерфейс класса A, от этого все эти данные не перестают быть локальными данными класса А.
Я говорю о том что эти сильные связи лучше оставлять внутри класса. Это наиболее подходящее для них место. Между классами надо стараться оставлять более слабые связи.
S>>То что ты, и Андрей называете внутренними неявными связями называется cohession (на русский обычно переводится как "зацепление"). IB>Ты ошибаешься.
Relational cohesion: average number of internal relationships per type:
H = (R+1) / N, where
R — number of type relationships internal to the package
N — number of types in the package
(c) Corillian Corp. (цитирую по бумажному источнику поэтому ссылки нет.
Там же у них есть и формула для кохесии отдельного типа, принцип тот же (она достаточно сложная лень переписывать).
S>>Да DI увеличивает связность и это оправдано, поскольку он решает ряд других проблем. IB>DI связность уменьшает. Ты все-таки посмотри определение связности.
Тут все зависит от сценария применения DI. Если у нас были два связанных класса и мы вводим DI то конечно связность уменьшается. Если у нас был всего один класс, и у нас изменились требования и нам надо ввести DI, то связность все же увеличится. Я имел ввиду именно этот сценарий.
S>>Надо приводить аргументы, а не "авторитетное мнение". IB>Ты сам просил "авторитетного мнения", напомнить? IB>А как "авторитетное мнение" не устраивает, начинаешь анекдоты вспоминать? Ну, хотелось чтоб и авторитетное и аргументированное, шоб просто пригвоздило меня и все IB>Аргументов я привел более чем достаточно.
Да.
S>>Да при том, что сериализация, сохранение в БД, UI не относятся к responsibility того класса, который мы обсуждаем. IB>Но они же все работают с состоянием? Очевидно, два твоих тезиса противоречят друг-другу.
S>> класс создавался для представления каких-то там данных, IB>Класс создавался для хранения данных (хранения части состояния системы). Очевидно(!) рассчет — это совсем другая ответственность.
S>>класс сообщает каков размер содержащихся в нем данных. Все это "свойственно" для данного класса. IB>По какой причине рассчет размера является "свойственным" для данного класса? На основании какого формального критерия ты так решил? Чем руководствовался?
S>>Это SRP. IB>По мне, так это нарушение SRP.
S>>Форматирование для сериализации — это не свойственно для класса, IB>Почему не свойственно-то? Еще раз, какой формальный критерий? IB>Доступ идет к одним и тем же данным, в одном и том же порядке, только логика при получении этих данных выполняется другая, но логика в любом случае должна быть инкапсулирована, так какая разница?
S>>поскольку это другой тип ответственности (один у него уже есть, другой надо выносить во внешний класс). IB>Почему другой-то?
Смотри, это проектирование. Ты сначала определяешь область ответственности и потом под нее делаешь класс. Все что не входит в эту область отвественности — гэть долой из класса.
Вот ты даже не заметил, что я определил область ответственности для калсса как "представление данных", а ты как "хранение данных". И сразу получаем разницу в трактовке того, что входит в эту ответственность, а что нет. Видно, что я определил более широкую ответственность а ты более узкую, вот и все. Соответственно мы получаем два разных дизайна (набора классов).
Четкий это критерий или не четкий, решай сам. Только вот ты выставляешь четкий критерий: все, что можно сделать через паблик интерфейс, выносим в другой класс. Вот String.Length, его можно получить через паблик интерфейс, но нет через внутреннее состояние будет быстрее, значит оставляем. Но ведь все, что можно получить через паблик интерфейс, можно получить и через внутреннее состояние. И четкий критерий уже начинает размываться.
S>>Дело в том что если foo() является функцией (в математическом смысле) исключительно только данных класса, то не зависимо от того, где ты ее разместишь, внутри самого класса, или вне, степень зависимости foo() от данных класса не изменяется. IB>Изменится степень твоего контроля над этой зависимостью. Связность страшна не сама по себе, а тем, что ты ее не контролируешь.
Как она изменится? Вероятно тем, что я смогу в функции получить доступ к приватным членам, больше никакой разницы нет. Функция стала частью интерфейса класса. Те классы что зависят от этой функции так же под контролем как и были. зависимости класса В от класса А теперь нет.
S>> Зато когда foo() находится внутри класса она входит в интерфейс самого класса и как ты говоришь, "любое внутреннее изменение, гарантировано не влияет на то, что находится за пределами контракта". То есть у нас все инкапсулировано. IB>Похоже ты все таки не понял мою аргументацию.. foo() уже не за пределами контракта, а значит любое изменение A может повлиять на foo() и наоборот.
Может повлиять а может не повлиять, что с того. В любом случае, если изменения семантики существенные будет ошибка компилятора. Если изменения не в семантике, на то модульные тесты есть.
S>> Мы можем менять и внутренне устройство данных класса и реализацию foo() и никакие другие классы это не затронет. IB>Ну да, просто Foo() ачнет выдавать неверные результаты или падать в рантайме... )
Твой вариант от этого тоже не застрахован.
S>>Если foo() лежит в другом классе, то появляется связь. IB>Она и раньше была, просто ты ее не видел и значит не мог контролировать. У тебя логика маленького зверька, который считает, что если закрыть глаза, то и его никто не увидит..
Даже так. Ладно, будем считать, что я этого не читал.
S>> Если нам понадобиться изменить алгоритм foo() то с большОй долей вероятности это выльется в изменение интерфейса класса A. IB>Да байта ради — это будут контролируеые и совершенно сознательные изменения. А вот если ты поменял A и забыл поменять foo(), что вполне возможно если их поместить вместе, то все будет гораздо печальнее.
Все изменения в коде должны быть контролируемые и сознательные . S>>Надеюсь, я достаточно подробно сформулировал свою точку зрения. IB>Угу, но свершенно неубедительно..
Взаимно
Здравствуйте, Ziaw, Вы писали:
Z>В том-то и дело, что я не уверен, что это косяк дизайна.
Ограничения, которых несложно избежать — однозначно косяк. Только этог уже выходит за рамки топика.
Z> Но оно же повысит стоимость кода который будет ей пользоваться.
За счет чего?
Z>Extension methods в принципе решают эту проблему и я использую их в подобных случаях. Но топик вроде бы не ограничен C# 3.0.
Ну, если сильно напряает чуть больший объем кода — оно конечно. Но по мне, так сложность поддержки намного хуже небольшого усложнения при начальном создании кода.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Ziaw, Вы писали:
Z>>Какие рекомендации будут по замене ORM?
AVK>Ну почему сразу замене? Можно и оставить. Тут главное — не выдавать косяки дизайна, вызванные кривостью ORM, за правильное архитектурное решение.
А тебе не приходило в голову, что для одного и того же кейса может быть несколько архитектурных решений и все правильные? Похоже нет.
Здравствуйте, stump, Вы писали:
S>А тебе не приходило в голову, что для одного и того же кейса может быть несколько архитектурных решений и все правильные?
Приходило. Только вот наличие двух методов в публичном контракте, делающих одно и то же — это точно неправильное решение.
S> Похоже нет.
Переходим на личности?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
Z>> Но оно же повысит стоимость кода который будет ей пользоваться. AVK>За счет чего?
За счет того, что писать и читать код
string result = param.Substring(2, 5).Trim().ToLower();
проще чем
string result = StringCase.ToLower(StringTransform.Trim(StringTransform.Substring(param, 2, 5)));
AVK>Ну, если сильно напряает чуть больший объем кода — оно конечно. Но по мне, так сложность поддержки намного хуже небольшого усложнения при начальном создании кода.
Этот код чаще всего более объемен чем доменная модель и изменяется/дописывается не реже. Он тоже требует той же, если не большей работе по поддержке.
Здравствуйте, Ziaw, Вы писали:
Z>Этот код чаще всего более объемен чем доменная модель и изменяется/дописывается не реже. Он тоже требует той же, если не большей работе по поддержке.
Вот как раз именно по тому что этот код объемен и часто меняется, его и лучше обособить и оградить контрактом, чтобы минимизировать неконтроллируемые корреляции с другими кусками кода.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
AVK>>>Требует доступа к внутреннему состоянию. A>>Это еще почему? AVK>А откуда еще взять длину строки? A>> Эту задачу вполне можно решить используя только публичный интерфейс класса string (без Lenght) перебором всех элементов, например. AVK>Нельзя — перформанс уже не тот. A>> То что это решение неэффективно другой вопрос. AVK>Нет, не другой, а этот самый.
Ага, в Строке нужно использовть внутренние более эффективные механизмы нахождения сисла символов (детей), а в точно такой-же задаче (топикстартера) нужен дополнительный класс; IndexOf() для Строки -- внутренний метод (из соображений производительности), а GetUniqChildName() (c тем же принципом) внешний.
Ну-ну.
AVK>>>Вопросы производительности требуют доступа к внутреннему состоянию. A>>Т.е. появились оговорки к "железному правилу"? AVK>Никаких оговорок. Приемлемая производительность не может быть реализована толькопубличным контрактом — метод нельзя помещать во внешний класс.
Что-то я этого предложения в правиле не заметил.
AVK>>>А вот тут правильно — ToUpper лучше было сделать статическим методом. A>>Лучше? По какому критерию? AVK>По тому, который в этом треде обсуждается.
Извините, но я как-то выпал из дискуссии (многабукаф -- ниасилил ). ИМХО, тут чего только не обсуждали. Не могли бы вы повторить.
AVK>Для удобства умные люди придумали extension methods.
Ну как-бы так сказать -- пока у меня к ним негативное отношение.
Создайте проект, который при запуске выводил бы на консоль 0. А после добавления ссылки на некоторую сборку выводил бы 1. Разница между двумя вариантами должна быть только в наличии ссылки на дополнительную сборку (никаких изменений в исходном коде), и оба варианта должны успешно компилироваться.
И после этого вы хотите сказать, что экстеншен методы это хорошо?
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, stump, Вы писали:
S>>А тебе не приходило в голову, что для одного и того же кейса может быть несколько архитектурных решений и все правильные?
AVK>Приходило. Только вот наличие двух методов в публичном контракте, делающих одно и то же — это точно неправильное решение.
Это методы разных классов, и не факт что они делают одно и то-же, да и моя реплика этого не касалась.
S>> Похоже нет.
AVK>Переходим на личности?
Вовсе нет, еще держусь, хотя некоторые по мне тут уже потоптались.
Просто в твоем ответе сквозит такая уверенность в существовании "правильной архитектуры". Слово "правильная" к архитектуре и дизайну софта вообще не применимо (ИМХО). Для любого более менее сложного случая, можно сделать несколько вариантов дизайна, и они вудут работать так требуется, и никакими метриками в рантайме не отличишь, какой код лучше работает.
Можно говорить об приемлимом дизайне, подходящем для данного случая, лучшем по сравнению с чем то другим, дающем преимущества в чем то и т.д. Но когда я слышу про правильную архитектуру, я стараюсь удержаться от улыбки.
Но это все мое ИМХО.
Здравствуйте, stump, Вы писали:
S>Просто в твоем ответе сквозит такая уверенность в существовании "правильной архитектуры". Слово "правильная" к архитектуре и дизайну софта вообще не применимо (ИМХО). Для любого более менее сложного случая, можно сделать несколько вариантов дизайна, и они вудут работать так требуется, и никакими метриками в рантайме не отличишь, какой код лучше работает. Но когда я слышу про правильную архитектуру, я стараюсь удержаться от улыбки.
Возможно просто под правильной архитектурой можно понимать разные рабочие и качественные варианты решения задачи. Необязательно имеется в виду какой-то один конкретный вариант.
Здравствуйте, AndrewVK, Вы писали:
AVK>Вот как раз именно по тому что этот код объемен и часто меняется, его и лучше обособить и оградить контрактом, чтобы минимизировать неконтроллируемые корреляции с другими кусками кода.
Спасибо, кое что прояснилось. Согласен, если вынос логики в хелпер обусловлен не соображениями использования методом приватных данных, а более четким ограничением контракта — он имеет смысл если имеет смысл само ограничение.
Здравствуйте, stump, Вы писали:
S>Давай каждый свои мысли будет продолжать
Тогда не останавливайся на пол-пути.. =)
S>Так ведь то что ты предлагаешь (b.foo()) это и есть "accessing local data of another module", т.е. самый сильный тип связности.
Ты не отличаешь local от public contract или просто прикидываешься?
S> Что с того что он все эти данные тягает через паблик интерфейс класса A, от этого все эти данные не перестают быть локальными данными класса А.
Перестают. Local — это именно приватные данные класса, его внутреннее состояние не доступное из вне.
S>Relational cohesion: average number of internal relationships per type:
Ты ошибаешься, мы говорим именно о связности, я тебе даже определение привел.
S>Тут все зависит от сценария применения DI.
Это ни коим образом не зависит от "сценария применения DI", уменьшение связности — это свойство DI вынесенное в его определение.
Он применяется, когда уже есть связь (внутренняя), чтобы вынести ее во внешний контракт и тем самым ослабить связность. То есть, сделать ровно то, о чем мы тут говорим.
S>Ну, хотелось чтоб и авторитетное и аргументированное, шоб просто пригвоздило меня и все
Не пригвоздило еще?
S>Смотри, это проектирование. Ты сначала определяешь область ответственности и потом под нее делаешь класс. Все что не входит в эту область отвественности — гэть долой из класса.
О, прикольно. То есть, запихнули все в класс, что влезно и назвали это одной ответственностью...
S>Вот ты даже не заметил, что я определил область ответственности для калсса как "представление данных", а ты как "хранение данных".
Я как раз заметил, и совершенно сознательно поправил на хранение. То есть, твоя ошибка в том, что ты не правильно определил область ответственности класса?
S>Четкий это критерий или не четкий, решай сам.
Совершенно не четкий.
S>Но ведь все, что можно получить через паблик интерфейс, можно получить и через внутреннее состояние. И четкий критерий уже начинает размываться.
Не начинает. Любая реализация должна пытаться обойтись публичным контрактом, если удалось — значит снаружи, если же по каким-то причинам (не важно по каким, производительность ли это или в публичном контракте чего-то не хватает), контрактом обойтись не удалось — значит внутри. Все очень просто и никаких неоднозначностей.
S>Как она изменится? Вероятно тем, что я смогу в функции получить доступ к приватным членам,
Именно.
S> Функция стала частью интерфейса класса. Те классы что зависят от этой функции так же под контролем как и были.
Нет. Ты больше не контролируешь связь между функцией и классом внутрь которого ее поместил.
S> зависимости класса В от класса А теперь нет.
Ее и раньше не было, точнее была контролируемая между A и B, а стала не контролируемая между A и foo().
S>Может повлиять а может не повлиять, что с того.
О, как ты заговорил.. Хорошо уже что признаешь проблему.
S>В любом случае, если изменения семантики существенные будет ошибка компилятора.
В том-то и дело, что совсем не факт, что будет.
S> Если изменения не в семантике, на то модульные тесты есть.
Модульные тесты не являюются панацеей и нельзя наличием модульных тестов оправдывать кривую архитектуру.
S>Твой вариант от этого тоже не застрахован.
Он застрахован от этого в большей степени.
S>Даже так. Ладно, будем считать, что я этого не читал.
Это любимый способ решать проблему?
Здравствуйте, MozgC, Вы писали:
MC>Я попросил показать реальный пример про String раз вы утверждаете что он реализован неправильно и эти функции надо было вынести в отдельные классы.
Мне сложно судить занятие чем, мешает тебе экстраполировать мой пример на класс String. =)
MC>Так и думал что вы так напишите. Как достойно с вашей стороны было прокомментировать это мое замечание.
Было бы недостойно оставить такое замечание без внимания..
Здравствуйте, Ziaw, Вы писали:
Z>Между тем мне лично ими удобнее пользоваться.
Лично мне все равно. У тебя же пользование DI дискомфорта не вызывает?
Но дело не в этом.
Z> Может быть это и есть причина по которой МС пошли на удорожание поддержки?
Вряд ли. Удобство весьма сомнительное, а проблем можно огрести достаточно.
Здравствуйте, IB, Вы писали:
MC>>Я попросил показать реальный пример про String раз вы утверждаете что он реализован неправильно и эти функции надо было вынести в отдельные классы. IB>Мне сложно судить занятие чем, мешает тебе экстраполировать мой пример на класс String. =)
Понял, реальных примеров привести не можете. Только голая теория.
— Ну вот надо все эти методы надо было вынести в отдельные классы, потому что в противном случае это косяк.
— Почему косяк?
— Ну вот связность, и все такое, и Мейерс заявил что "они ошибаются"
— На практике в чем косяк?
— Ну я же писал.. что косяк..
— Так в чем косяк на практие?
— Ну связность.. и Мейерс говорил..
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Давай каждый свои мысли будет продолжать IB>Тогда не останавливайся на пол-пути.. =)
Ага...
S>>Так ведь то что ты предлагаешь (b.foo()) это и есть "accessing local data of another module", т.е. самый сильный тип связности. IB>Ты не отличаешь local от public contract или просто прикидываешься?
foo() использует локальные данные класса А, то что ты заставил ее работать через паблик интерфейс не меняет сути.
S>> Что с того что он все эти данные тягает через паблик интерфейс класса A, от этого все эти данные не перестают быть локальными данными класса А. IB>Перестают. Local — это именно приватные данные класса, его внутреннее состояние не доступное из вне.
Т.е. данные класса, опубликованные через паблик интерфейс перестают быть данными класса. Оч интересно.
S>>Relational cohesion: average number of internal relationships per type: IB>Ты ошибаешься, мы говорим именно о связности, я тебе даже определение привел.
Это не я ошибаюсь. Это была цитата.
S>>Тут все зависит от сценария применения DI. IB>Это ни коим образом не зависит от "сценария применения DI", уменьшение связности — это свойство DI вынесенное в его определение. IB>Он применяется, когда уже есть связь (внутренняя), чтобы вынести ее во внешний контракт и тем самым ослабить связность. То есть, сделать ровно то, о чем мы тут говорим.
Разница в трактовке роли внутренних связей. Но против этого мне нечего возразить, убедил.
S>>Ну, хотелось чтоб и авторитетное и аргументированное, шоб просто пригвоздило меня и все IB>Не пригвоздило еще?
Неа..
S>>Смотри, это проектирование. Ты сначала определяешь область ответственности и потом под нее делаешь класс. Все что не входит в эту область отвественности — гэть долой из класса. IB>О, прикольно. То есть, запихнули все в класс, что влезно и назвали это одной ответственностью...
Опять начинаешь... Черным по зеленому написано сначала определяешь ответственность, потом на этом критерии принимаешь решение что включать, а что нет.
S>>Вот ты даже не заметил, что я определил область ответственности для калсса как "представление данных", а ты как "хранение данных". IB>Я как раз заметил, и совершенно сознательно поправил на хранение. То есть, твоя ошибка в том, что ты не правильно определил область ответственности класса?
Не ошибка, а точка знения, не "неправильно" а по-другому. Тут в другой ветке обсуждали дизайн System.String (http://www.rsdn.ru/Forum/message/3031451.aspx
) и обнаружили что предлагаемый правильный дизайн намного неудобнее использовать чем существующий "неправильный".
S>>Но ведь все, что можно получить через паблик интерфейс, можно получить и через внутреннее состояние. И четкий критерий уже начинает размываться. IB>Не начинает. Любая реализация должна пытаться обойтись публичным контрактом, если удалось — значит снаружи, если же по каким-то причинам (не важно по каким, производительность ли это или в публичном контракте чего-то не хватает), контрактом обойтись не удалось — значит внутри. Все очень просто и никаких неоднозначностей.
S>>Как она изменится? Вероятно тем, что я смогу в функции получить доступ к приватным членам, IB>Именно.
S>> Функция стала частью интерфейса класса. Те классы что зависят от этой функции так же под контролем как и были. IB>Нет. Ты больше не контролируешь связь между функцией и классом внутрь которого ее поместил.
Почему это. Ты эту фразу уже раз шесть повторил как заклинание и не разу не объяснил, где же там конкретно контроль теряется? Компилятор соответствие типов или наличие членов престает там контролировать? S>> зависимости класса В от класса А теперь нет. IB>Ее и раньше не было, точнее была контролируемая между A и B, а стала не контролируемая между A и foo().
Ну вот опять. Контролируемая — неконтролируемая. Чем неконтролируемая связь отличается от контролируемой. В каких конкретно аспектах теряется контроль
S>>Может повлиять а может не повлиять, что с того. IB>О, как ты заговорил.. Хорошо уже что признаешь проблему.
Хорошо что ты заметил. Я не ортодокс.
S>>В любом случае, если изменения семантики существенные будет ошибка компилятора. IB>В том-то и дело, что совсем не факт, что будет.
Че за компилятор такой?
S>> Если изменения не в семантике, на то модульные тесты есть. IB>Модульные тесты не являюются панацеей и нельзя наличием модульных тестов оправдывать кривую архитектуру.
Конечно. Вот отсутствие модульных тесов можно оправдывать кривой архитектурой В-общем это офтоп.
S>>Твой вариант от этого тоже не застрахован. IB>Он застрахован от этого в большей степени.
S>>Даже так. Ладно, будем считать, что я этого не читал. IB>Это любимый способ решать проблему?
Это была твоя проблема, что ты не можешь себя сдерживать в рамках приличий.
Здравствуйте, stump, Вы писали:
AVK>>Приходило. Только вот наличие двух методов в публичном контракте, делающих одно и то же — это точно неправильное решение. S>Это методы разных классов, и не факт что они делают одно и то-же
А если они делают не годно и то же, тогда для продолжения обсуждения нужна информация о том, чем они отличаются и для чего это нужно. Нодиайн в любом случае негодный — понять тому, кто это будет применять, почему там два метода невозможно, можно только запомнить.
S>, да и моя реплика этого не касалась.
Ты отвечал на мое сообщение, в котором была ровно одна фраза.
AVK>>Переходим на личности? S>Вовсе нет
Вовсе да. Я тебя уже просил не делать выводов о квалификации собеседников.
S>, еще держусь, хотя некоторые по мне тут уже потоптались.
Не знаю кто по тебе потоптался, но лично я пока аналогичных выводов о твоей квалификации не делал, так чтот попрошу в общении со мной от подобных приемчиков воздержаться.
S>Просто в твоем ответе сквозит такая уверенность в существовании "правильной архитектуры"
Тебе показалось. Если что то предполагаешь — не надо гадать и заниматься демагогией, можно просто прямо задать вопрос.
S>Слово "правильная" к архитектуре и дизайну софта вообще не применимо (ИМХО). Для любого более менее сложного случая, можно сделать несколько вариантов дизайна, и они вудут работать так требуется, и никакими метриками в рантайме не отличишь, какой код лучше работает.
Это вопрос, совсем далекий от темы топика. Если интересно его обсудить — заводи отдельный топик.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Aikin, Вы писали:
A>Ага, в Строке нужно использовть внутренние более эффективные механизмы нахождения сисла символов (детей), а в точно такой-же задаче (топикстартера) нужен дополнительный класс; IndexOf() для Строки -- внутренний метод (из соображений производительности), а GetUniqChildName() (c тем же принципом) внешний.
A>Ну-ну.
Что ну-ну. Принцип сформирован предельно ясно — если можно сделать внешним, значит нужно сделать внешним. Не надо к нему додумывать несуществующих дополнений, а потом радостно их опровергать. Не ищи подвоха там, ге его нет. Если перформанс критичен (а в случае string он критичнее некуда) — значит нужно это учитывать. То, что перформанс очень часто конфликтует с хорошим дизайном — ну что поделаешь. Надо искать золотую середину, когда и дизайн не слишком плох, и перформанс приемлем.
AVK>>Никаких оговорок. Приемлемая производительность не может быть реализована толькопубличным контрактом — метод нельзя помещать во внешний класс. A>Что-то я этого предложения в правиле не заметил.
А оно там не нужно. В правиле не конкретизируется вообще, по какой причине не получается пользоваться только внешним контрактом. А если не конкретизируется — значит причина может быть любой.
AVK>>По тому, который в этом треде обсуждается. A>Извините, но я как-то выпал из дискуссии (многабукаф -- ниасилил
Придется осилить. Работать аннотатором у меня никакого желания нет. И за падоначью лексику здесь банят.
AVK>>Для удобства умные люди придумали extension methods. A>Ну как-бы так сказать -- пока у меня к ним негативное отношение.
Это, уж извини, твои личные проблемы.
A>Чего стоит только мозгоразрывательный этюд:
Эти этюды малоинтересны с точки зрения практического использования.
A>И после этого вы хотите сказать, что экстеншен методы это хорошо?
Да.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Ziaw, Вы писали:
Z>>Между тем мне лично ими удобнее пользоваться. IB>Лично мне все равно. У тебя же пользование DI дискомфорта не вызывает?
Вызывает, но по совокупности воздействий добавляет больше комфорта.
Да и прямой аналогии DI с сабжем я не вижу.
Z>> Может быть это и есть причина по которой МС пошли на удорожание поддержки? IB>Вряд ли. Удобство весьма сомнительное, а проблем можно огрести достаточно.
Т.е. все архиекторы библиотек работы со строками .net, java, python, ruby допустили одну и ту же ошибку дизайна?
Меня больше устраивает другая версия: они все проектировали так, чтобы людям было удобнее ее использовать.
Видимо посчитали, что вам будет все равно, а мне приятно
AVK>А оно там не нужно. В правиле не конкретизируется вообще, по какой причине не получается пользоваться только внешним контрактом. А если не конкретизируется — значит причина может быть любой.
А вы спросили топикстартера о том, что для него критично, а что нет? Нет, не спросили. Вы сразу стали гнуть свою линию.
О том что в этой линии есть исключения (ну как бы даже не исключения, а уточнения к самому определению, и даже не к определению, а к некоторым словам, ...) стало известно много ниже.
AVK>>>По тому, который в этом треде обсуждается. A>>Извините, но я как-то выпал из дискуссии (многабукаф -- ниасилил
AVK>Придется осилить. Работать аннотатором у меня никакого желания нет.
Извините, но вышеозначенную "грызню" читать и выкапывать оттуда здравые мысли не имею никакого желания.
Если это необходимое условие продолжения ветвии. Давайте на этом закончим
AVK> И за падоначью лексику здесь банят.
Хочется нехорошее сказать, но промолчу.
Здравствуйте, Aikin, Вы писали:
AVK>>А оно там не нужно. В правиле не конкретизируется вообще, по какой причине не получается пользоваться только внешним контрактом. А если не конкретизируется — значит причина может быть любой. A>А вы спросили топикстартера о том, что для него критично, а что нет? Нет, не спросили. Вы сразу стали гнуть свою линию.
Во-первых не сразу, во-вторых топик стартер спрашивал кто как поступает, а не как ему самому сделать.
A>О том что в этой линии есть исключения (ну как бы даже не исключения, а уточнения к самому определению, и даже не к определению, а к некоторым словам, ...) стало известно много ниже.
На то и голова, чтобы не принимать все бездумно. Никакие принципы и правила от ее применения не спасают.
AVK>> И за падоначью лексику здесь банят. A>Хочется нехорошее сказать, но промолчу.
Промолчи.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, stump, Вы писали:
S>foo() использует локальные данные класса А, то что ты заставил ее работать через паблик интерфейс не меняет сути.
Меняет. В одном случае все взаимодействие проходит через публичный контракт, а в другом нет, как следствие, в одном случае метод защищен от внутренних изменений класса (и наоборот), а в другом не защищен.
S>Т.е. данные класса, опубликованные через паблик интерфейс перестают быть данными класса.
Если доступ к локальным данным осуществляется через публичный интерфейс, то это доступ к публичному интерфейсу, а не к локальным данным.
S>Это не я ошибаюсь. Это была цитата.
Просто она была не к месту. =)
S>Разница в трактовке роли внутренних связей. Но против этого мне нечего возразить, убедил.
То есть, за DI ты признаешь право уменьшить звязность путем вынесения внутренней связи во внешний контракт, а за просто выносом метода — нет?
S>Черным по зеленому написано сначала определяешь ответственность, потом на этом критерии принимаешь решение что включать, а что нет.
Что мешает определить ответственность как "все что делает эта программа"?
S> Тут в другой ветке обсуждали дизайн System.String (http://www.rsdn.ru/Forum/message/3031451.aspx
) и обнаружили что предлагаемый правильный дизайн намного неудобнее использовать чем существующий "неправильный".
Кто это обнаружил? На мой взгляд использовать оба подхода по удобству совершенно одинаково, а уж при наличии методов расширения разница вообще стирается.
S>Почему это. Ты эту фразу уже раз шесть повторил как заклинание и не разу не объяснил, где же там конкретно контроль теряется? Компилятор соответствие типов или наличие членов престает там контролировать?
Компилятор перестает следить за доступом к приватным переменным.
S>Вот отсутствие модульных тесов можно оправдывать кривой архитектурой В-общем это офтоп.
Ну почему же офтоп. Как раз если обсуждаемый метод вынесен наружу класса, его намного проще протестировать, подсунув вместо обрабатываемого класса какой-нибудь mock, так что очень в тему замечание, насчет unit-тестирования...
S>Это была твоя проблема, что ты не можешь себя сдерживать в рамках приличий.
Что я такого неприличного написал?!
Здравствуйте, Aikin, Вы писали:
A>А вы спросили топикстартера о том, что для него критично, а что нет? Нет, не спросили.
У "топикпастера" все очевидно.
A>О том что в этой линии есть исключения
Нету там исключений.
A> (ну как бы даже не исключения, а уточнения к самому определению, и даже не к определению, а к некоторым словам, ...) стало известно много ниже.
Что именно стало известно?
A>Извините, но вышеозначенную "грызню" читать и выкапывать оттуда здравые мысли не имею никакого желания.
Тогда зачем вообще вмешиваешься?
Здравствуйте, MozgC, Вы писали:
MC>Понял, реальных примеров привести не можете.
Я привел вполне реальный пример, много примеров. Я не виноват, что они тебе не нравятся. =)
MC>- Ну вот надо все эти методы надо было вынести в отдельные классы, потому что в противном случае это косяк. MC>- Почему косяк? MC>- Ну вот связность, и все такое, и Мейерс заявил что "они ошибаются" MC>- На практике в чем косяк? MC>- Ну я же писал.. что косяк.. MC>- Так в чем косяк на практие? MC>- Ну связность.. и Мейерс говорил..
Это ты сейчас с кем говорил?
Здравствуйте, Ziaw, Вы писали:
Z>Да и прямой аналогии DI с сабжем я не вижу.
Да прямее уже и не найдешь. Обсуждаемые хелперы — DI сервисы в чистом виде.
Z>Т.е. все архиекторы библиотек работы со строками .net, java, python, ruby допустили одну и ту же ошибку дизайна?
Не знаю, всех не смотрел, но в .Net ошибки дизайна присутствуют в количестве.
Z>Меня больше устраивает другая версия:
Да байта ради..
Z>они все проектировали так, чтобы людям было удобнее ее использовать.
То есть, единственное возражение "не удобно использовать"?
Ок.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>foo() использует локальные данные класса А, то что ты заставил ее работать через паблик интерфейс не меняет сути. IB>Меняет. В одном случае все взаимодействие проходит через публичный контракт, а в другом нет, как следствие, в одном случае метод защищен от внутренних изменений класса (и наоборот), а в другом не защищен.
Его не надо защищать от этого. Он имеет полное право обращаться к этим данным.
S>>Это не я ошибаюсь. Это была цитата. IB>Просто она была не к месту. =)
Значит ты ее не прочел.
S>>Разница в трактовке роли внутренних связей. Но против этого мне нечего возразить, убедил. IB>То есть, за DI ты признаешь право уменьшить звязность путем вынесения внутренней связи во внешний контракт, а за просто выносом метода — нет?
Я признал то что твоя трактовка непротиворичива. Однако она не соответствует тому, как трактуют связность инструменты разработчика Visual Studio, Corillian. Внутренние связи, которых ты так сильно боишся там не учитываются. И это выглядит логично, связность не влияет на рантайм, эта характеристика важна при внесении изменений в код и для использования этого кодв. Вносить изменения в класс, большая часть логики которого раскидана по хелперам в соответствии с твоим принципом неудобно. Пользоваться — тоже.
S>>Черным по зеленому написано сначала определяешь ответственность, потом на этом критерии принимаешь решение что включать, а что нет. IB>Что мешает определить ответственность как "все что делает эта программа"?
Мне? Здравый смысл.
S>> Тут в другой ветке обсуждали дизайн System.String (http://www.rsdn.ru/Forum/message/3031451.aspx
) и обнаружили что предлагаемый правильный дизайн намного неудобнее использовать чем существующий "неправильный". IB>Кто это обнаружил? На мой взгляд использовать оба подхода по удобству совершенно одинаково, а уж при наличии методов расширения разница вообще стирается.
А на мой взгляд — нет. Речь не идет о методах расширения.
S>>Почему это. Ты эту фразу уже раз шесть повторил как заклинание и не разу не объяснил, где же там конкретно контроль теряется? Компилятор соответствие типов или наличие членов престает там контролировать? IB>Компилятор перестает следить за доступом к приватным переменным.
Неубедительно. У тебя есть заклинание "только через публичный контракт" — поэтому для тебя доступ к приватным переменным, это трагедия. У меня такого заклинания нет, я все проверяю на критерии "нельзя ли все это сделать еще проще", и "удобно ли этим пользоваться".
S>>Это была твоя проблема, что ты не можешь себя сдерживать в рамках приличий. IB>Что я такого неприличного написал?!
Даже не заметил...
В общем и целом картина ясна. Мне твои аргументы понятны. Признаю за ними право на жизнь. Более того, неоднократно встречал подобный подход на практике, приходилось и ревьюить и майнтейнить подобный код. Не могу сказать что это пуля.
Я предпочитаю не увлекаться хелперами. Впрочем, свою позицию я тут уже подробно изложил.
Считаю дискуссию исчерпаной. Спасибо.
Здравствуйте, stump, Вы писали:
S>Его не надо защищать от этого.
Надо.
S>Он имеет полное право обращаться к этим данным.
Имеет, но через публичный контракт.
S>Я признал то что твоя трактовка непротиворичива.
Это не моя трактовка, это общепринятая, из википедии.
S>Однако она не соответствует тому, как трактуют связность инструменты разработчика Visual Studio, Corillian.
Очень даже соответствует. На твоем же примере VS показала, что Maintainability Index у класса с вкряченным методом хуже. Какие тебе еще доказательства нужны?
S> Вносить изменения в класс, большая часть логики которого раскидана по хелперам в соответствии с твоим принципом неудобно.
Удобно, очень удобно, уж поверь, я даже пример приводил.
S>Пользоваться — тоже.
На вкус и цвет. Для ярых приверженцев определенного стиля есть методы расширения.
S>Мне? Здравый смысл.
Ну, собственно, с чего начали — наш формальный критерий называется "здравый смысл". Я не отрицаю, штука полезная, но на формальный критерий не тянет никак.
S>А на мой взгляд — нет.
То есть, все возражения свелись к мелкому неудобству при использовании.
S>У тебя есть заклинание "только через публичный контракт"
Это не заклинание, это фактор который ты упорно игнорируешь. За всю дискуссию ты не привел ни одного возражения, просто молча пропускал, поэтому я вынужден был повторять это снова и снова.
S>Считаю дискуссию исчерпаной. Спасибо.
Заходите еще..
Здравствуйте, MozgC, Вы писали:
MC>А почему вы считаете что ответственность класса Order это только держать данные для заказа? Так можно начать про что угодно говорить. Давайте скажем что ответственность класса String — держать набор символов, а все методы нужно вынести в хелперный класс (сервис) и будет у нас StringService.GetStringLength(), StringService.IndexOf(), StringService.StringToUpper() и т.д.
Кстати, именно так Александреску и советует делать для С++, где изобретение собственных строк — это народная забава.
Здравствуйте, Ziaw, Вы писали:
AVK>>Если у тебя в публичном интерфейсе OrderLines есть метод Add — AddOrderLine следует вынести в dev/null Z>А то, что он сделан таким для совместимости с ORM — в топку ORM?
Да, так как это плохой ORM.
Здравствуйте, Ziaw, Вы писали:
Z>Какие рекомендации будут по замене ORM? Полный отказ, поиск, покупка и освоение ORM который сможет работать с правильным контрактом или написание своего?
Тот же Hibernate нормально с подчинёнными коллекциями работает.
Z>Может я не вижу очевидного решения которое не затормозит разработку на несколько человекомесяцев и будет удовлетворять всем канонам дизайна?
А поправить сам ORM нельзя?
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Мне? Здравый смысл. IB>Ну, собственно, с чего начали — наш формальный критерий называется "здравый смысл". Я не отрицаю, штука полезная, но на формальный критерий не тянет никак.
Кому нужны формальные критерии? Да и топикстартер не спрашивал про формальные критерии. В программировании есть множество формальных критереев, которые совсем необъективны.
Формальные критерии — не результат, а только материал для анализа.
S>>А на мой взгляд — нет. IB>То есть, все возражения свелись к мелкому неудобству при использовании.
Каждое мелкое неудобство в использовании рано иили поздно становится ошибкой в программе.
S>>У тебя есть заклинание "только через публичный контракт" IB>Это не заклинание, это фактор который ты упорно игнорируешь. За всю дискуссию ты не привел ни одного возражения, просто молча пропускал, поэтому я вынужден был повторять это снова и снова.
Заклинание "только через публичный контракт" плохо работает для эволюционного проектирования.
"только через публичный контракт" иногда может сильно повлиять на быстродействие.
"только через публичный контракт" не стоит применять для методов, изменяющих состояние объекта. Не знаю как вы, а я сильно удивляюсь если вызов A.B(x) меняет состояние x.
Здравствуйте, gandjustas, Вы писали:
G>Кому нужны формальные критерии?
Прочитав топик, не сложно убедиться, что мой оппонент утверждал, что его критерий нужно помещать метод в код или нет — чудо как четок и формален. Как выяснилось он ошибался.
G>Да и топикстартер не спрашивал про формальные критерии.
Он — нет, но дискуссия развернулась именно в этом направлении.
G> В программировании есть множество формальных критереев, которые совсем необъективны.
Это не имеет отношения к данному обсуждению.
G>Формальные критерии — не результат, а только материал для анализа.
В данном случае, формальный критерий — результат очень тщательного анализа.
G>Каждое мелкое неудобство в использовании рано иили поздно становится ошибкой в программе.
Не буду топтаться на этом, весьма спорном тезисе, замечу только, что погоня за удобствами ведет к куда большим ошибкам.
G>Заклинание "только через публичный контракт" плохо работает
Заклинания вообще плохо работают, за заклинаниями не ко мне.
G> для эволюционного проектирования.
Доказательства будут?
G>"только через публичный контракт" иногда может сильно повлиять на быстродействие. G>"только через публичный контракт" не стоит применять для методов, изменяющих состояние объекта. G>Не знаю как вы, а я сильно удивляюсь если вызов A.B(x) меняет состояние x.
У меня ощущение, что ты не очень внимательно прочитал топик и сильно попутал о чем речь.
Напомню еще раз: Речь не о том, что бы во что бы то ни стало работать через публичный контракт (хотя по возможности надо стараться пользоваться именно публичным контрактом), а о том, что если метод работает с классом через публичный контракт, то он должен находиться вне класса.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, gandjustas, Вы писали:
G>>Кому нужны формальные критерии? IB>Прочитав топик, не сложно убедиться, что мой оппонент утверждал, что его критерий нужно помещать метод в код или нет — чудо как четок и формален. Как выяснилось он ошибался.
Формальность и объективность не одно и тоже.
G>>Формальные критерии — не результат, а только материал для анализа. IB>В данном случае, формальный критерий — результат очень тщательного анализа.
Одно другому не мешает.
G>>Каждое мелкое неудобство в использовании рано иили поздно становится ошибкой в программе. IB>Не буду топтаться на этом, весьма спорном тезисе, замечу только, что погоня за удобствами ведет к куда большим ошибкам.
Не надо гнаться за удобством, но и жертвовать удобством в счет "правильности" тоже не лучшее решение.
G>> для эволюционного проектирования. IB>Доказательства будут?
При эволюционном проектировани публичные контракты могут меняться достаточно часто.
G>>"только через публичный контракт" иногда может сильно повлиять на быстродействие. G>>"только через публичный контракт" не стоит применять для методов, изменяющих состояние объекта. G>>Не знаю как вы, а я сильно удивляюсь если вызов A.B(x) меняет состояние x. IB>У меня ощущение, что ты не очень внимательно прочитал топик и сильно попутал о чем речь. IB>Напомню еще раз: Речь не о том, что бы во что бы то ни стало работать через публичный контракт (хотя по возможности надо стараться пользоваться именно публичным контрактом), а о том, что если метод работает с классом через публичный контракт, то он должен находиться вне класса.
Этот тезис не противоречит вызовам A.B(x), менящюим x.
Здравствуйте, minorlogic, Вы писали:
M>Следуя твоей логике наименьшая связанность будет у программы с main и одним классом/функцией. Очевидный для меня абсурд.
Ну угадал. Локальные функции рулят Может Хейлсберг когда-нибудь родит что-нибудь подобное в шарпе.
Неясность изложения обычно происходит от путаницы в мыслях.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, gandjustas, Вы писали:
Несколько замечаний не по предмету, а по ходу дискуссии.
G>>Кому нужны формальные критерии? IB>Прочитав топик, не сложно убедиться, что мой оппонент утверждал, что его критерий нужно помещать метод в код или нет — чудо как четок и формален. Как выяснилось он ошибался.
Это выяснилось только лишь для тебя. Дело в том что я говорил о множестве критериев, как в пользу выноса функции из класса, так и в пользу помещения ее в класс. Большинство из них просто выпало из обсуждения благодаря твоей забавной манере вести дискуссию. Ты усиленно опровергаешь все, что противоречит твоей точке зрения, а на остальное просто не обращаешь внимания. G>>Да и топикстартер не спрашивал про формальные критерии. IB>Он — нет, но дискуссия развернулась именно в этом направлении.
Поэтому дискуссия и равернулась в данном направлении. G>> В программировании есть множество формальных критереев, которые совсем необъективны. IB>Это не имеет отношения к данному обсуждению.
Именно это и обсуждается сейчас. G>>Формальные критерии — не результат, а только материал для анализа. IB>В данном случае, формальный критерий — результат очень тщательного анализа.
Многие уже привели примеры когда твой формальный критерий не работает.
G>> для эволюционного проектирования. IB>Доказательства будут?
Были уже. Потрудись перечитать аргументацию оппонентов. G>>"только через публичный контракт" иногда может сильно повлиять на быстродействие. G>>"только через публичный контракт" не стоит применять для методов, изменяющих состояние объекта. G>>Не знаю как вы, а я сильно удивляюсь если вызов A.B(x) меняет состояние x. IB>У меня ощущение, что ты не очень внимательно прочитал топик и сильно попутал о чем речь. IB>Напомню еще раз: Речь не о том, что бы во что бы то ни стало работать через публичный контракт (хотя по возможности надо стараться пользоваться именно публичным контрактом), а о том, что если метод работает с классом через публичный контракт, то он должен находиться вне класса.
Увы, но убедительного обоснования этого тезиса так и не прозвучало.
Здравствуйте, IB, Вы писали:
IB>Да прямее уже и не найдешь. Обсуждаемые хелперы — DI сервисы в чистом виде.
Injection означает то, что зависимости задаются снаружи, а не прибиты гвоздями как статик хелпер.
Хотя с определенного уровня абстракции можно увидеть признаки "DI времени кодирования" .
Z>>они все проектировали так, чтобы людям было удобнее ее использовать. IB>То есть, единственное возражение "не удобно использовать"?
Собственно да. Мне нравится идеология базовых библиотек ruby. Я знаю, что практически все что мне нужно сделать с объектом я сделаю с помощью его собственных методов. Чаще всего в конвеерном стиле цепочки вызовов. Возможно у меня дурной вкус, но я рад, что C# 3.0 дает мне возможность делать похожий дизайн.
Я согласен с тем, что код оперирующий лишь публичным контрактом можно вынести в отдельный модуль.
Я согласен с тем, что в C# 3.0, при использовании extension methods это можно делать для любого такого метода (в ruby кстати тоже).
Я согласен с тем, что это может удешевить поддержку продукта.
Я категорически не согласен с тем, что "нет разницы между вызовом экземплярных методов и методов статик хелперов".
Я не согласен с тем, что удобством использования класса можно пренебречь при его проектировании.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Здравствуйте, stump, Вы писали:
S>>Теперь, если кто умеет, посчитайте метрики связности кода для случая с классом OrderCalcHelper и без него. CRA>Очень мне было интересно, прикинуть метрики. Вот результат расчета VS2008: CRA>С Helper'ом CRA>Namespace: Test CRA>Maintainability Index: 93 CRA>Cyclomatic Complexity: 19 CRA>Depth of Inheritance: 1 CRA>Class Coupling: 7 CRA>Lines of Code: 22
CRA>Без Helper'а CRA>Namespace: Test CRA>Maintainability Index: 92 CRA>Cyclomatic Complexity: 18 CRA>Depth of Inheritance: 1 CRA>Class Coupling: 6 CRA>Lines of Code: 21
CRA>Получается по расчетам VS, сопровождаемость программы с Helper'ом лучше чем без него
на 1 процентный пункт.
Без хелпера связность 6, с хэлпером связность 7. В примере хэлпер увеличил связность на 17%. Что и требовалось доказать.
Здравствуйте, gandjustas, Вы писали:
G>Формальность и объективность не одно и тоже.
Еще раз повторить? Оппонент утверждал, что его критерий формален — выяснилось, что ни разу.
Что здесь не объективного?
G>Одно другому не мешает.
И что ты хочешь этим сказать?
G>Не надо гнаться за удобством, но и жертвовать удобством в счет "правильности" тоже не лучшее решение.
В данном случае "правильность" не абстрактна и может быть выражена совершенно конкретными циферками, в чем легко убедиться опять-таки внимательно прочитав топик.
G>При эволюционном проектировани публичные контракты могут меняться достаточно часто.
Тогда наоборот, такой подход изумительно подходит для эволюционного проектирования, так как помимо всего прочего еще и компилятор следит за тем, что публичный контракт поменялся, а тот кто им пользуется — еще нет.
G>Этот тезис не противоречит вызовам A.B(x), менящюим x.
Ты любой код меняющий x помещаешь в x? UI например? Надеюсь, все таки нет... Правильный вызов будет выглядет так: var newX = A.B(oldX);
Здравствуйте, stump, Вы писали:
S>Это выяснилось только лишь для тебя.
Это выяснилось для всех, кто дал себе труд внимательно прочитать топик. Ни одного строгого критерия ты так и не привел, все ограничилось здравым смыслом. Я, безусловно, не отрицаю его пользу, но перефразируя классику, здравым смыслом и формальным критерием можно добиться гораздо большего, чем просто здравым смыслом.
S>Дело в том что я говорил о множестве критериев, как в пользу выноса функции из класса, так и в пользу помещения ее в класс.
Ты совершенно четко сказал, что у тебя есть чудо как формальный критерий позволяющий определить вносить функцию в класс или нет, но сформулировать его ты так и не смог.
S> Большинство из них просто выпало из обсуждения благодаря твоей забавной манере вести дискуссию.
После того как аргументы кончились, ты начинаешь ссылаться на мою манеру ведения дискуссии. Я, кстати, не первый это замечаю.
Не находишь, что если с манерой что-то не так, то наверное все-таки не у меня?
Я, конечно, бываю временами несколько резок, но только если оппонент совсем пургу несет или занимается откровенной демагогией.
S> Ты усиленно опровергаешь все, что противоречит твоей точке зрения, а на остальное просто не обращаешь внимания.
Это упрек? Очевидно я буду опровергать то, что не соответствует моей точке зрения и спокойно пропускать все остальное, так как это поддерживает мою позицию.. При этом я вполне поддаюсь убеждению, если аргументы здравые и внятные.
В данном случае, на протяжении всей дискуссии, был только один относительно внятный контраргумент — "некоторым неудобно использовать".
S>Именно это и обсуждается сейчас.
Какое отношение другие формальные критерии в CS, имеют отношение к данному конкретному критерию?
S>Многие уже привели примеры когда твой формальный критерий не работает.
Да ты что?! Где? Ни одного примера не было приведено, ни тобой, ни другими участниками дискуссии.
S>Были уже. Потрудись перечитать аргументацию оппонентов.
Не было. Я очень внимательно читаю весь топик. Вся аргументация оппонентов свелась к "пользуйтесь здравым смыслом" и "неудобно использовать".
S>Увы, но убедительного обоснования этого тезиса так и не прозвучало.
Прозвучали более чем убедительные доказательства, как на словах, так и с отсылкой на классиков. То что они тебе не нравятся, уже не моя вина.
Здравствуйте, stump, Вы писали:
S>на 1 процентный пункт.
Так там и кода на наперсток.
S>Без хелпера связность 6, с хэлпером связность 7.
Ты все напутал, не "связность", а "связь между классами". Тебе еще раз определение связности привести?
S>Что и требовалось доказать.
В итоге ты доказал ровно обратное. Главная метрика — это как раз индекс поддерживаемости. Большая связность, в конечном итоге, плоха именно плохой поддерживаемостью, и именно это тебе студия и насчитала.
Здравствуйте, Ziaw, Вы писали:
Z>Injection означает то, что зависимости задаются снаружи, а не прибиты гвоздями как статик хелпер.
Так никто и не призывает оставлять статик хелпер. Я уже расписывал, как происходит эволюция кода вынесенного за приделы класса, в конечном итоге он перерождается в полноценный DI сервис, умеющий работать с семейством объектов, из которого его изначально вынесли.
Очень способствует и гибкости, и удобству поддержки, и повторной используемости кода.
Z>Хотя с определенного уровня абстракции можно увидеть признаки "DI времени кодирования" .
Это просто промежуточный рефакторинг, помогающий перейти от захардкоженного в класс функционала к DI.
IB>>То есть, единственное возражение "не удобно использовать"? Z>Собственно да.
Уфф..
Z>Я знаю, что практически все что мне нужно сделать с объектом я сделаю с помощью его собственных методов. Чаще всего в конвеерном стиле цепочки вызовов.
А отлаживать ты это дело пробовал? Лично для меня что b.A().A().A(), что B.A(B.A(b)) — выглядят одинаково криво, ровно потому, что поставить точку останова в нужном месте, большая проблема — это все к вопросу об удобстве.
Z>Я категорически не согласен с тем, что "нет разницы между вызовом экземплярных методов и методов статик хелперов".
см. выше, никто не призывает оставлять все статик хелперами.
Z>Я не согласен с тем, что удобством использования класса можно пренебречь при его проектировании.
"Удобство" штука весьма относительная. С функциональными языками по первости возиться знаешь как неудобно?
"Ты их в дверь — они в окно...
нет ребята, с этим делом мы покончили давно" (с) В.Высоцкий
IB>Здравствуйте, stump, Вы писали:
S>>на 1 процентный пункт. IB>Так там и кода на наперсток.
S>>Без хелпера связность 6, с хэлпером связность 7. IB>Ты все напутал, не "связность", а "связь между классами". Тебе еще раз определение связности привести?
Написано "Class Coupling: 6"
Написано: "In computer science, coupling or dependency is the degree to which each program module relies on each one of the other modules. Coupling is usually contrasted with cohesion. Low coupling often correlates with high cohesion, and vice versa." (Wikipedia)
В метрике связности число связей в числителе, число модулей в знаменателе. О чем тут еще можно спорить?
S>>Что и требовалось доказать. IB>В итоге ты доказал ровно обратное. Главная метрика — это как раз индекс поддерживаемости. Большая связность, в конечном итоге, плоха именно плохой поддерживаемостью, и именно это тебе студия и насчитала.
Это ты ее объявил главной. Какие метрики важнее — это очень специфично и зависит от конкретного проекта и случая. Но не в этом дело. Метрика "Maintainability Index" изменилась на 1 при значении 92, а метрика "Class Coupling" изменилась на 1 при значении 6. То есть удельное изменение первой метрики составило 1.08%, а второй метрики 16.66%. Очевидно что изменение дизайна оказало наибольшее влияние на метрику "Class Coupling", при том, что остальные метрики кода остались практически неизменными. Следовательно, это изменение дизайна (вынос методов в хелпер класс) не изменяет функциональности и целенаправленно воздействует только на одну метрику кода, которая называется "Class Coupling" увеличивая ее значение в данном случае на 16.66%. Название метрики "Class Coupling" переводится как "связность классов".
Я просто поражен тем, что мне приходится вот так вот разжевывать эти очевидные вещи.
Это просто анализ метрик кода, я даже не делаю выводов хорошо это или плохо. Просто данный анализ опровергает многие из твоих утверждений, и в отличии от них он основан на инструментальных измерениях конкретного примера кода.
Здравствуйте, IB, Вы писали:
IB>Это просто промежуточный рефакторинг, помогающий перейти от захардкоженного в класс функционала к DI.
Моя очередь сказать: Уфф...
Если в данном рефакторинге есть необходимость — нормальный прием.
IB>А отлаживать ты это дело пробовал? Лично для меня что b.A().A().A(), что B.A(B.A(b)) — выглядят одинаково криво, ровно потому, что поставить точку останова в нужном месте, большая проблема — это все к вопросу об удобстве.
В студии это отлаживается дюже просто — через выделение нужного стека вызовов и quickwatch.
При условии, что нет побочных эффектов повторного вызова, а к этому всегда следует стремиться.
Если они есть придется слегка править код:
var result = b.A().A();
result = result.A();
Z>>Я категорически не согласен с тем, что "нет разницы между вызовом экземплярных методов и методов статик хелперов". IB>см. выше, никто не призывает оставлять все статик хелперами.
Ключевое слово здесь хелпер, а не статик.
Z>>Я не согласен с тем, что удобством использования класса можно пренебречь при его проектировании. IB>"Удобство" штука весьма относительная. С функциональными языками по первости возиться знаешь как неудобно?
Не знаю
Первое соприкосновение с лиспом в бытность студентом особых проблем не вызвало (хотя могу что-то не вспонмнить сейчас). Сразу стал писать как на императивном языке, потом понемногу приходило понимание какие плюсы дает функциональный стиль.
Эти плюсы остаются плюсами при применении в императивных приложениях.
Цепочки вызвов легко читаются программистами не программировавшими на ФЯ, у них есть несомненные достоинства,
1. порядок вызвов идет слева направо/сверху вних, в отличии от из центра вложенности к границам.
2. дополнительные аргументы переданные в методы видны невооруженным взглядом
Я вполне осознаю пользу рефакторинга в DI сервисы, но также вижу вред в ухудшении читабельности кода и увеличении количества сущностей которыми должен оперировать не резиновый мозг программиста.
Здравствуйте, IB, Вы писали:
MC>>Понял, реальных примеров привести не можете. IB>Я привел вполне реальный пример, много примеров. Я не виноват, что они тебе не нравятся. =)
Где ты там увидел пример про проблемы с классом String (как ты говоришь)? Просто 2 предложения теории, к тому же к классу String не имеющей никакого отношения. Я задал несколько раз конкретный вопрос, попросил привести КОНКРЕТНЫЙ пример про проблемы с классом STRING, раз ты говоришь что он неправильно спроектирован. Ты либо не читаешь вопрос, либо не понимаешь, либо не можешь привести пример, либо у тебя такая манера вести дискуссию, когда тебе задают один конкретный вопрос, а ты отвечаешь совсем другое, причем не раз.
MC>>- Ну вот надо все эти методы надо было вынести в отдельные классы, потому что в противном случае это косяк. MC>>- Почему косяк? MC>>- Ну вот связность, и все такое, и Мейерс заявил что "они ошибаются" MC>>- На практике в чем косяк? MC>>- Ну я же писал.. что косяк.. MC>>- Так в чем косяк на практие? MC>>- Ну связность.. и Мейерс говорил.. IB>Это ты сейчас с кем говорил?
К сожалению, это ты так ведешь дискуссию.
Здравствуйте, Аноним, Вы писали:
А>Есть класс, который реализует некую ф-ть А>class Object А>У него есть методы, типа А>GetName — имя А>GetSize — возвращает свой размер без учета детей А>GetChilds — возвращает список своих детей
А>Появилась необходимость написать ряд методов, типа расчета размера рекурсивно с учетом всех детей, генерации уникального имени среди детей и т.д А>Все эти методы могут быть реализованы за счет публичного интерфейса Object, поэтому включать их в класс Object не хочется. Пока свалил их как внешние ф-ции в отдельный фаил ObjectHelper в виде CalcFullObjectSize, GetObjectUniqChildName, но выглядит это не очень красиво... А>Кто как поступает с такими методами?
Кто-нибудь может доходчиво объяснить, зачем класть эти функции в еще один класс? Классы же нельзя расширять. Даже в .NET с ее extension methods, если я правильно понимаю, нельзя запихивать в объект статические члены.
Так вот, что мешает просто сделать свободные функции? Или этот класс живет в королевстве существительных? А то я так и представляю, что кто-то создал Object и ObjectHelper, а потом кому-то понадобилось CalcFullObjectSizeInPetabytes, он создает ObjectHelperHelper, который ссылается на ObjectHelper и его функции, и т. д.
Тем более, что свободные функции всё равно вылезут. Например, кому-то захочется знать количество детей, или имя в такой-то кодировке, он и напишет функции countChildren(Object o) { return o.GetChildren().size(); }, getNameUtf8(Object o) { return convert(o.GetName(), "UTF-8"); }.
Здравствуйте, Roman Odaisky, Вы писали:
RO>Кто-нибудь может доходчиво объяснить, зачем класть эти функции в еще один класс? Классы же нельзя расширять.
А зачем расширять статические классы?
RO> Даже в .NET с ее extension methods, если я правильно понимаю, нельзя запихивать в объект статические члены.
Что значит запихивать?
RO>Так вот, что мешает просто сделать свободные функции?
Отсутствие таковых в шарпе и джаве, к примеру.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, stump, Вы писали:
S>Написано "Class Coupling: 6"
Class Coupling — связь между классами.
S>Написано: "In computer science, coupling
Просто Coupling — связность.
S> О чем тут еще можно спорить?
Вот и я не понимаю, о чем: Написано же английским по белому " Low coupling refers to a relationship in which one module interacts with another module through a stable interface and does not need to be concerned with the other module's internal implementation"
Так вот interacts with another module through a stable interface — это и есть тот самый class coupling, который считает студия и в его большем количестве ничего плохого нет. А concerned with the other module's internal implementation — выражается в Maintainability index-е.
Хватит уже отговорки придумывать.
S>Это ты ее объявил главной.
У нас разговор о простоте поддержки кода. Class Coupling на нее мало влияет, а вот Maintainability index имеет самое прямое и непосредственное отношение, он по сути это и меряет — простоту поддержки кода.
S> Метрика "Maintainability Index" изменилась на 1 при значении 92, а метрика "Class Coupling" изменилась на 1 при значении 6.
Какая разница насколько изменилась метрика Class Coupling, если она не имеет никакого отношения к делу?
Давай опять по новой. Если следовать твоей логике, то DI — увеличивает связность, так как при использовании DI увеличивается Class Coupling по метрикам студии.
S> Очевидно что изменение дизайна оказало наибольшее влияние на метрику "Class Coupling",
Проблема только в том, что эта метрика имеет весьма посредственное отношение к обсуждаемому вопросу.
S>Название метрики "Class Coupling" переводится как "связность классов".
Class Coupling меряет связи классов через публичный контракт, которые считаются "хорошими" с точки зрения связности.
S>Я просто поражен тем, что мне приходится вот так вот разжевывать эти очевидные вещи.
А уж я-то как поражен! Просто не верится, что ты к таким слабым отмазкам прибегаешь...
S> Просто данный анализ опровергает многие из твоих утверждений,
Он не опровергает, а как раз подтверждает. Очень хорошо подтверждает, надеюсь, объяснил почему?
Давай еще раз, если не понятно. По неизвестной мне причине, ты, под связностью, понимаешь число связей между классами, тогда как вся прогрессивная общественность (см википедию) понимает под этим число неявных связей. При этом считается, что перевод неявных связей в явные связность уменьшает — надеюсь не надо объяснять почему? (Для примера см DI, который как раз этим и занимается)
Теперь давай разберем твой пример: Понятно, что связь между методом и классом есть всегда, явная она или не явная. Если метод внутри класса, то связь неявная, что выражается в меньшем числе метрики Class Coupling и меньшем Maintainability index-е, понятно, что поддерживать неявную связь сложнее чем явную. Как только мы вынесли метод наружу, увеличилось число явных связей (увеличилась метрика Class Coupling) и, как следствие, увеличилась метрика Maintainability index, говорящая о том, что такой код проще поддерживать.
ЧТД.
S>он основан на инструментальных измерениях конкретного примера кода.
И мне он этим тоже нравится..
Здравствуйте, MozgC, Вы писали:
MC>Где ты там увидел пример про проблемы с классом String (как ты говоришь)?
Там пример про проблемы со всеми классами такого рода.
MC> Просто 2 предложения теории, к тому же к классу String не имеющей никакого отношения.
Я не виноват, что ты не в состоянии экстраполировать пример с одного класса на другой.
MC>Я задал несколько раз конкретный вопрос, попросил привести КОНКРЕТНЫЙ пример про проблемы с классом STRING, раз ты говоришь что он неправильно спроектирован.
Ну назови в примере топик пастера класс String, если тебя это смущает.
MC>Ты либо не читаешь вопрос,
Я уже раз пять ответил на вопрос, я не виноват, что тебе ответ не нравится.
MC>К сожалению, это ты так ведешь дискуссию.
Боюсь, что это ты ее так читаешь..
Здравствуйте, AndrewVK, Вы писали:
RO>>Кто-нибудь может доходчиво объяснить, зачем класть эти функции в еще один класс? Классы же нельзя расширять. AVK>А зачем расширять статические классы?
Чтобы положить еще функций для работы с тем классом.
RO>> Даже в .NET с ее extension methods, если я правильно понимаю, нельзя запихивать в объект статические члены. AVK>Что значит запихивать?
В .NET же можно сделать void SetHello(this String s) { s = "Hello"; }, и появится метод String.SetHello. А вот если у тебя есть ObjectHelper и ты хочешь, чтобы там появилась еще пара (статических) функций, то это даже с extension methods не выйдет (или я неправ?). Какой-то беспорядок получается: одни функции живут в Object, другие — в ObjectHelper, третьи — еще в каком-то классе.
В C++, например, это выглядело бы так:
// library codenamespace NS
{
class Object
{
SomeCollection getChildren();
};
size_t getFullSize(Object const &);
}
// user code
size_t getFullSizeInPetabytes(NS::Object const &);
// и здесь функции вызываются одинаково, компилятор сам их вытащит оттуда, откуда нужно:
NS::Object o;
getFullSize(o);
getFullSizeInPetabytes(o);
RO>>Так вот, что мешает просто сделать свободные функции? AVK>Отсутствие таковых в шарпе и джаве, к примеру.
А автор где-то указал язык программирования?
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, gandjustas, Вы писали:
G>>Формальность и объективность не одно и тоже. IB>Еще раз повторить? Оппонент утверждал, что его критерий формален — выяснилось, что ни разу. IB>Что здесь не объективного?
Необъективен сам критерий, независимо от формальности и неформальности.
G>>Одно другому не мешает. IB>И что ты хочешь этим сказать?
То что сама по себе цифирка ни о каком качестве кода не говорит.
G>>Не надо гнаться за удобством, но и жертвовать удобством в счет "правильности" тоже не лучшее решение. IB>В данном случае "правильность" не абстрактна и может быть выражена совершенно конкретными циферками, в чем легко убедиться опять-таки внимательно прочитав топик.
См выше.
G>>При эволюционном проектировани публичные контракты могут меняться достаточно часто. IB>Тогда наоборот, такой подход изумительно подходит для эволюционного проектирования, так как помимо всего прочего еще и компилятор следит за тем, что публичный контракт поменялся, а тот кто им пользуется — еще нет.
Только на мелкое исправление контракта получается много исправлений методов в разных классах.
Если методы находятся внутри класса, то исправления локальны.
Мелочь, а приятно.
G>>Этот тезис не противоречит вызовам A.B(x), менящюим x. IB>Ты любой код меняющий x помещаешь в x?
Метод, который принимает x в качестве параметра и его действия заключаются в измененении x, я помещу в класс x.
IB>UI например?
Что например?
IB>Надеюсь, все таки нет... Правильный вызов будет выглядет так: var newX = A.B(oldX);
А если нельзя объект копировать?
Здравствуйте, Roman Odaisky, Вы писали:
RO>>>Кто-нибудь может доходчиво объяснить, зачем класть эти функции в еще один класс? Классы же нельзя расширять. AVK>>А зачем расширять статические классы? RO>Чтобы положить еще функций для работы с тем классом.
Что значит положить? Дописать просто функцию в существующий исходник класса? Никаких проблем. Добавить функцию в рантайме? Какой смысл? Добавить функцию в скомпилированный уже класс? Такое невозможно ни с каким классом в подавляющем большинстве компилируемых языков.
AVK>>Что значит запихивать? RO>В .NET же можно сделать void SetHello(this String s) { s = "Hello"; }
Можно.
RO>, и появится метод String.SetHello.
Нет, не появится. Просто этот метод можно будет вызывать через точку от аргумента, не более того.
RO> А вот если у тебя есть ObjectHelper и ты хочешь, чтобы там появилась еще пара (статических) функций, то это даже с extension methods не выйдет
А зачем это нужно? Честно говоря, я впервые о таком пожелании слышу.
RO> Какой-то беспорядок получается: одни функции живут в Object, другие — в ObjectHelper, третьи — еще в каком-то классе.
Беспорядок, он исключительно в голове архитекта. Если там бардак, то и в коде бардак.
RO>В C++, например, это выглядело бы так: RO>[c] RO>// library code
RO>namespace NS RO>{ RO> class Object RO> { RO> SomeCollection getChildren(); RO> };
RO> size_t getFullSize(Object const &); RO>}
RO>// user code
RO>size_t getFullSizeInPetabytes(NS::Object const &);
RO>// и здесь функции вызываются одинаково, компилятор сам их вытащит оттуда, откуда нужно:
Перегрузка с extension methods тоже возможна, только не глобально по имени, а в контексте типа аргумента с учетом наследования и реализации интерфейсов. Если тебя интересуют конкретные правила, на основании которых производится выбор среди всех доступных методов экземпляра и extension methods — самым лучшим будет ознакомиться с соответствующей частью стандарта C#.
AVK>>Отсутствие таковых в шарпе и джаве, к примеру. RO>А автор где-то указал язык программирования?
Судя по pascal casing методов и статическим классам — с большой долей вероятности это все же C#.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, gandjustas, Вы писали:
G>Только на мелкое исправление контракта получается много исправлений методов в разных классах.
Контракт первичен. Если его надо править, значит его надо править в любом случае, вне зависимости от наличия хелперов.
IB>>UI например? G>Что например?
Ну, например нужно показать форму, в которой можно подредактировать некий класс А. оформлено это все ввиде функции, которой на вход передается единственный параметр с экземпляром редактируемого класса. Куда эту функцию помещаем?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, stump, Вы писали:
S>Здравствуйте, Ага, Вы писали:
Ага>>А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше? S>Нет, давай останемся в рамках первоначальных условий. Все мы понимаем, что изменение требований влечет изменение дизайна.
Это лишь пример, который показывает, что GetTotal и GetSubtotal, возможно, имеют такое же отношение к Customer и Country как и к Order. А следовательно не должны являться членами всех выше перечисленных сущностей.
Кроме того, если при изменении требований приходится серьезно изменять дизайн и переписывать половину существующего код ( а в данном случае так и будет), то это плохой дизайн.
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, Ага, Вы писали:
Ага>>А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше?
Z>Не аргумент. Они потребуют практически одинаковой работы по переделке дизайна. TaxRate станет внешним параметром.
А для меня аргумент. TaxRate и не был свойством продукта, как и GetTotal/GetSubtotal не были методами Order/OrderLine. Поэтому и понадобятся изменения в дизайне. Если бы эти обязанности изначально назначены правильным сущностям, то изменения не потребовались бы. Дизайн должен помогать реализовывать требования, а не мешать.
Здравствуйте, IB, Вы писали:
IB>Там пример про проблемы со всеми классами такого рода. IB>Я не виноват, что ты не в состоянии экстраполировать пример с одного класса на другой.
MC>>Я задал несколько раз конкретный вопрос, попросил привести КОНКРЕТНЫЙ пример про проблемы с классом STRING, раз ты говоришь что он неправильно спроектирован. IB>Ну назови в примере топик пастера класс String, если тебя это смущает.
Нафига мне его как-то называть?
И Причем тут экстраполировать, я тебе задал КОНКРЕТНЫЙ вопрос про КОНКРЕТНЫЕ проблемы с КОНКРЕТНЫМ классом. Раз не можешь дать конкретный ответ, то так и напиши что не хочешь, или не можешь, или что конкретно ты не встречал проблем с классом String. Ты не способен адекватно и конкретно ответить на вопрос? Если не способен, то не отвечай, это будет лучше чем отвечать не в тему.
Здравствуйте, Ага, Вы писали:
Ага>>>А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше?
Ага>А для меня аргумент. TaxRate и не был свойством продукта, как и GetTotal/GetSubtotal не были методами Order/OrderLine. Поэтому и понадобятся изменения в дизайне. Если бы эти обязанности изначально назначены правильным сущностям, то изменения не потребовались бы. Дизайн должен помогать реализовывать требования, а не мешать.
Надо как-то менее противоречиво формулировать мысли.
Здравствуйте, AndrewVK, Вы писали:
RO>> А вот если у тебя есть ObjectHelper и ты хочешь, чтобы там появилась еще пара (статических) функций, то это даже с extension methods не выйдет AVK>А зачем это нужно? Честно говоря, я впервые о таком пожелании слышу.
RO>> Какой-то беспорядок получается: одни функции живут в Object, другие — в ObjectHelper, третьи — еще в каком-то классе. AVK>Беспорядок, он исключительно в голове архитекта. Если там бардак, то и в коде бардак.
Ну представь себе. Ты используешь библиотеку, в ней есть класс Object и статический класс ObjectHelper со всякими внешними функциями для Object. Ты хочешь написать еще пару внешних функций для действий, которые тебе нужно выполнять над Object для решения своих задач. Куда ты их положишь? А если ты пишешь библиотеку, опирающуюся на Object/ObjectHelper, то ты будешь поставлять, кроме Object и ObjectHelper, еще и ObjectHelper2?
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
G>>Только на мелкое исправление контракта получается много исправлений методов в разных классах. AVK>Контракт первичен. Если его надо править, значит его надо править в любом случае, вне зависимости от наличия хелперов.
Это один из взглядов. На мой взгяд первичен функционал программы.
IB>>>UI например? G>>Что например?
AVK>Ну, например нужно показать форму, в которой можно подредактировать некий класс А. оформлено это все ввиде функции, которой на вход передается единственный параметр с экземпляром редактируемого класса. Куда эту функцию помещаем?
При функциональном проектировании не будет формы, которая редактирует какой-то объект. Да и куча других проблем есть у такой организации кода.
Здравствуйте, gandjustas, Вы писали:
G>>>Только на мелкое исправление контракта получается много исправлений методов в разных классах. AVK>>Контракт первичен. Если его надо править, значит его надо править в любом случае, вне зависимости от наличия хелперов. G>Это один из взглядов. На мой взгяд первичен функционал программы.
Контракт первичен по отношению к реализации. При чем тут функционал? Функционал не просто первичен, он определяет что нам нужно.
AVK>>Ну, например нужно показать форму, в которой можно подредактировать некий класс А. оформлено это все ввиде функции, которой на вход передается единственный параметр с экземпляром редактируемого класса. Куда эту функцию помещаем? G>При функциональном проектировании не будет формы, которая редактирует какой-то объект.
А что будет?
G> Да и куча других проблем есть у такой организации кода.
Какой такой?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Roman Odaisky, Вы писали:
RO>Ну представь себе. Ты используешь библиотеку, в ней есть класс Object и статический класс ObjectHelper со всякими внешними функциями для Object. Ты хочешь написать еще пару внешних функций для действий, которые тебе нужно выполнять над Object для решения своих задач. Куда ты их положишь?
В собственный хелпер с extension методами в отдельбном неймспейсе.
RO> А если ты пишешь библиотеку, опирающуюся на Object/ObjectHelper, то ты будешь поставлять, кроме Object и ObjectHelper, еще и ObjectHelper2?
Да, только не ObjectHelper2, а что нибудь более отражающее сущность моей библиотеки.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
MC>>а AndrewVK, говорит что нельзя — "перфоманс уже не тот". IB>AVK говорит, что данный метод для своей корректной работы (например из соображений производительности), должен иметь доступ к приватным переменным класса => этот метод имеет полное право быть членом класса.
Иван, у тебя получается достаточно странная логика. С одной стороны высокоуровневые рассуждения о связности (я их не опровергаю, даже за) о том как важно разделять и властвовать, с другой стороны, когда речь дошла до практики, вдруг начинаются прыжки в сторону на тему "для эффективной реализации, метод должен быть членом класса".
У тебя получается некоторый String с методами, которые остались внутри просто потому что их нельзя выдернуть не просадив производительность (абсолютно нелепая выйдет компания) и помойка для всех остальных методов в хелпере. Мне это совсем не видится светлым будущим и, думаю, надо, всё же, чётко разделить теорию и практику. Раскидать методы по N классам достигнув абстрактной крутости и создав абсолютно нелогичный API не представляет мне достойной целью.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Написано "Class Coupling: 6" IB>Class Coupling — связь между классами.
S>>Написано: "In computer science, coupling IB>Просто Coupling — связность.
Остальную часть цитаты отрезал чтобы не мешала полемизировать и делать "правильные" выводы.
S>> О чем тут еще можно спорить? IB>Вот и я не понимаю, о чем: Написано же английским по белому " Low coupling refers to a relationship in which one module interacts with another module through a stable interface and does not need to be concerned with the other module's internal implementation" IB>Так вот interacts with another module through a stable interface — это и есть тот самый class coupling, который считает студия и в его большем количестве ничего плохого нет.
Как раз есть, это очень плохо. Полхо так же то что ты не можешь этого понят, зациклившись на ничем не подкрепленных правилах.
S>>Это ты ее объявил главной. IB>У нас разговор о простоте поддержки кода. Class Coupling на нее мало влияет, а вот Maintainability index имеет самое прямое и непосредственное отношение, он по сути это и меряет — простоту поддержки кода.
Class coupling сильно влияет на простоту поддержки. Цитата из MSDN :
Avoid excessive class coupling
TypeName
AvoidExcessiveClassCoupling
CheckId
CA1506
Category
Microsoft.Maintainability
Breaking Change
Breaking
Cause
A type or method is coupled with many other types.
Rule Description
This rule measures class coupling by counting the number of unique type references that a type or method contains.
Types and methods with a high degree of class coupling can be difficult to maintain. It is a good practice to have types and methods that exhibit low coupling and high cohesion.
How to Fix Violations
To fix this violation, try to redesign the type or method to reduce the number of types to which it is coupled.
When to Suppress Warnings
Exclude this warning when the type or method is still considered maintainable, despite its large number of dependencies on other types.
Избегайте чрезмерной связанности классов!
Высокая связанность классов (high degree of class coupling — подчаркиваю) делает затруднительной поддержку.
Хорошей практикой является создание типов демонстрирующих высокую кохессию (к кохессии мы еще вернемся) и низкую связность.
Так вот, использование "четкого принципа": "методы которые работают через публичный контракт класса должны быть вынесены за пределы этого класса", неизбежно и автоматически ведет к повышению class coupling и уменьшению кохессии.
Но в MSDN, к счастью сидят не столь твердолобые ребята, о чем свидетельствует последний абзац приведенной цитаты.
S>> Метрика "Maintainability Index" изменилась на 1 при значении 92, а метрика "Class Coupling" изменилась на 1 при значении 6. IB>Какая разница насколько изменилась метрика Class Coupling, если она не имеет никакого отношения к делу?
Оказывается имеет, прямое отношение см. выше. IB>Давай опять по новой. Если следовать твоей логике, то DI — увеличивает связность, так как при использовании DI увеличивается Class Coupling по метрикам студии.
Мы обсуждаем конкретные метрики конкретного кода, давай не будем "заводить рака за угол".
S>> Очевидно что изменение дизайна оказало наибольшее влияние на метрику "Class Coupling", IB>Проблема только в том, что эта метрика имеет весьма посредственное отношение к обсуждаемому вопросу.
Проблема в том что ты не можешь понять основных принципов анализа метрик кода, и меня это удивляет. Ты не видишь что показывают метрики. Их надо оценивать интегрально. И они показывают какие характеристики кода как измениолись. В данном случае метрики показали что введение хелпера практически ни на что не повлияло (не повлияло и на Maintainability Index) кроме связности классов, оно ее резко увеличило.
Можно провести дальнейший анализ, а почему Maintainability Index остался на месте когда class coupling полез вверх? Да потому что пример достаточно прост и переносимые методы очень элементарны.
Но. пример показал тенденцию. И ты этого не хочешь замечать, видимо по тому что эта тенденция тебе не нравится.
S>>Название метрики "Class Coupling" переводится как "связность классов". IB>Class Coupling меряет связи классов через публичный контракт, которые считаются "хорошими" с точки зрения связности.
Даже если онии хорошие, излишнее количество этих связей рано или поздно начнет создавать проблемы. S>> Просто данный анализ опровергает многие из твоих утверждений, IB>Он не опровергает, а как раз подтверждает. Очень хорошо подтверждает, надеюсь, объяснил почему? IB>Давай еще раз, если не понятно. По неизвестной мне причине, ты, под связностью, понимаешь число связей между классами, тогда как вся прогрессивная общественность (см википедию) понимает под этим число неявных связей. При этом считается, что перевод неявных связей в явные связность уменьшает — надеюсь не надо объяснять почему? (Для примера см DI, который как раз этим и занимается)
Ага теперь про твои любимые "неявные связи". Вся прогрессивная общественность называет это кохессией (зацеплением). И она тоже бывает хорошей и плохой:
Cohesion is a measure of how strongly-related or focused the responsibilities of a single class are. In object-oriented programming, if the methods that serve the given class tend to be similar in many aspects the class is said to have high cohesion. In a highly-cohesive system, code readability and the likelihood of reuse is increased, while complexity is kept manageable.
Сужествуют различные типы кохессии. Рассматриваемый нами тип (метод использует данные класса) можно отнести к комуникативной и частично функциональной кохессии
Communicational cohesion
Communicational cohesion is when parts of a module are grouped because they operate on the same data (e.g. a module which operates on the same record of information).
Functional cohesion (best)
Functional cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module (e.g. parsing XML in the case of Expat (XML)).
...
communicational and sequential cohesion are very good; and functional cohesion is superior.
(все цитаты из википедии)
Т.е. размещение методов котоорые оперируют данными класса внутри самого класса имеет смысл и может дать положительный эффект. Сейчас ты скажешь "давай продолжим твою мысль и разместим все методы в одном классе". И все станет очень плохо:
Coincidental cohesion (worst)
Coincidental cohesion is when parts of a module are grouped arbitrarily (at random); the parts have no significant relationship (e.g. a module of frequently used mathematical functions).
(и то иногда приходится так поступать).
IB>Теперь давай разберем твой пример: Понятно, что связь между методом и классом есть всегда, явная она или не явная. Если метод внутри класса, то связь неявная, что выражается в меньшем числе метрики Class Coupling и меньшем Maintainability index-е, понятно, что поддерживать неявную связь сложнее чем явную. Как только мы вынесли метод наружу, увеличилось число явных связей (увеличилась метрика Class Coupling) и, как следствие, увеличилась метрика Maintainability index, говорящая о том, что такой код проще поддерживать.
Очень натянутые выводы, плохой анализ. Любой видит, что с выносом методов в хелпер Maintainability index увеличился на 1% и за это заплачено значительным увеличением class coupling.
Я к чему веду?
Я не утверждаю, что есть "четкий принцип" — "если метод использует публичный контракт класса — он должен быть размещен в данном классе."
Я не утверждаю, что есть "четкий принцип" — "если метод использует публичный контракт класса — он должен быть вынесен в другой класс."
Я утверждаю, что есть оба принципа, и применительно к конкретным условиям может быть предпочтителен один или другой. И в ходе дискуссии было рассмотрено немало кейсов для обоих.
А вот тупое (в смысле прямолинейное и бездумное) следование доному из них, ни к чему хорошему привести не может.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
G>>>>Только на мелкое исправление контракта получается много исправлений методов в разных классах. AVK>>>Контракт первичен. Если его надо править, значит его надо править в любом случае, вне зависимости от наличия хелперов. G>>Это один из взглядов. На мой взгяд первичен функционал программы.
AVK>Контракт первичен по отношению к реализации. При чем тут функционал? Функционал не просто первичен, он определяет что нам нужно.
А то что нам нужно в итоге определяет контракты.
AVK>>>Ну, например нужно показать форму, в которой можно подредактировать некий класс А. оформлено это все ввиде функции, которой на вход передается единственный параметр с экземпляром редактируемого класса. Куда эту функцию помещаем? G>>При функциональном проектировании не будет формы, которая редактирует какой-то объект.
AVK>А что будет?
Что нибудь другое.
G>> Да и куча других проблем есть у такой организации кода. AVK>Какой такой?
Когда экранные формы привязываются к объектам.
Здравствуйте, Ага, Вы писали:
Ага>Это лишь пример, который показывает, что GetTotal и GetSubtotal, возможно, имеют такое же отношение к Customer и Country как и к Order. А следовательно не должны являться членами всех выше перечисленных сущностей.
Вообще-то сумма по заказу это свойство заказа, не зависимо от чего зависит расчет этой суммы, где расположен метод, который ее считает. и вообще считается она или хранится в где нибудь.
Есть такое понятие "предметная область программы". В предметной области сумма заказа есть всойство заказа. У многих программистов распространено мнение что дизайн программы может влиять на предметную область. Весьма печальное заблуждение, оно обычно приводит к несоответствию готовой программы функциональным требованиям.
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, Ага, Вы писали:
Ага>>>>А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше?
Ага>>А для меня аргумент. TaxRate и не был свойством продукта, как и GetTotal/GetSubtotal не были методами Order/OrderLine. Поэтому и понадобятся изменения в дизайне. Если бы эти обязанности изначально назначены правильным сущностям, то изменения не потребовались бы. Дизайн должен помогать реализовывать требования, а не мешать.
Z>Надо как-то менее противоречиво формулировать мысли.
Что тут противоречивого? Была неверна построена модель предметной области, только и всего. Фраза "TaxRate теперь не свойство продукта" в бизнес требовании -- для облегчения понимания изменений.
Здравствуйте, stump, Вы писали:
S>Здравствуйте, Ага, Вы писали:
Ага>>Это лишь пример, который показывает, что GetTotal и GetSubtotal, возможно, имеют такое же отношение к Customer и Country как и к Order. А следовательно не должны являться членами всех выше перечисленных сущностей.
S>Вообще-то сумма по заказу это свойство заказа, не зависимо от чего зависит расчет этой суммы, где расположен метод, который ее считает. и вообще считается она или хранится в где нибудь.
В предметной области -- да, но это не означает, что класс заказа должен ее вычислять и/или хранить. Об этом и речь.
S>Есть такое понятие "предметная область программы". В предметной области сумма заказа есть всойство заказа. У многих программистов распространено мнение что дизайн программы может влиять на предметную область. Весьма печальное заблуждение, оно обычно приводит к несоответствию готовой программы функциональным требованиям.
А это к чему?
Спор шел о местонахождении функций в классе/хелпере.
Вы усложнили условия задачи:
А теперь представь, появилось новое бизнес требование . TaxRate теперь не свойство продукта, а вычисляется в зависимости от клиента (страна/штат и т.д, физическое лицо/юридическое лицо, и т.д.). Теперь какой подход лучше?
Я высказал мысль, что в этих обстоятельствах оба подхода потребуют одинаковых трудозатрат по изменению дизайна.
Я ошибочно предполагал, что вы остаетесь в рамках топика.
Оказалось, что вы начинали искать изначальные ошибки вымышленной модели вымышленного бизнеспроцесса на основании вымышленных вами изменений этого самого процесса.
И спрашиваете меня Ага>По существу есть что сказать?
Все что было у меня сказать по существу я сказал всем кто вел диалог по существу.
Заниматься вымышленным жизненным циклом вымышленного ПО предоставлю вам.
Здравствуйте, Ziaw, Вы писали:
Z>Оказалось, что вы начинали искать изначальные ошибки вымышленной модели вымышленного бизнеспроцесса на основании вымышленных вами изменений этого самого процесса.
Все зависимости от правдивости данного замечания, плюсмильён за сарказм. Я чрезвычайно хохотался.
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Какой такой? G>>Когда экранные формы привязываются к объектам.
AVK>Функция редактирования в форме получает на вход экземепляр? Получает. Меняет состояние? Меняет. Значит, согласно твоему критерию: AVK>
Метод, который принимает x в качестве параметра и его действия заключаются в измененении x, я помещу в класс x.
AVK>метод редактирования объекта в UI должен быть помещен в класс, который редактируется.
Не передергивай. Такое решение быдет пристрелено раньше, чем дело дойдет до конкретных методов.
Стоит рассматривать более полную картину в таком случае.
Здравствуйте, gandjustas, Вы писали:
AVK>>Функция редактирования в форме получает на вход экземепляр? Получает. Меняет состояние? Меняет. Значит, согласно твоему критерию: AVK>>
Метод, который принимает x в качестве параметра и его действия заключаются в измененении x, я помещу в класс x.
AVK>>метод редактирования объекта в UI должен быть помещен в класс, который редактируется. G>Не передергивай.
А я и не передергиваю. Я тебе задал конкретный вопрос, на который ты уже третье сообщение никак не можешь ответить.
G> Такое решение быдет пристрелено раньше, чем дело дойдет до конкретных методов.
Почему? Какое будет не пристрелено?
G>Стоит рассматривать более полную картину в таком случае.
Картина достаточно полная. Простейшая задача — есть класс, его нужно редактировать в UI. Точка. Для простоты можешь считать, что это и есть все приложение.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
G>>Стоит рассматривать более полную картину в таком случае. AVK>Картина достаточно полная. Простейшая задача — есть класс, его нужно редактировать в UI. Точка. Для простоты можешь считать, что это и есть все приложение.
Здравствуйте, stump, Вы писали:
S>Вообще-то сумма по заказу это свойство заказа, не зависимо от чего зависит расчет этой суммы, где расположен метод, который ее считает. и вообще считается она или хранится в где нибудь.
Очень интересная мысль. Которая, позволяет сделать кое-какие выводы (ИМХО):
Сумма заказа — свойство заказа (с этим очень трудно поспорить)
А вот Алгоритм расчета стоимости заказа, явно уже не отностится к отвественности абстракции "Заказ"
По этому получается такая вот загагулина:
Это гуд:
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal Total{get;set;}
}
///Может быть Хелпером либо Иньекциейpublic class OrderTotalCalculator
{
public decimal UpdateTotal(Order order){}
}
Это не гуд:
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal GetTotal()
{
//get totals from order lines
}
}
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, C...R...a...S...H, Вы писали:
CRA>> public decimal UpdateTotal(Order order){}
AVK>Лучше все же CalcTotal,а не UpdateTotal.
Думаю, что давать имя CalcTotal, следует в случае IoC
В случае же хелпера в после подходит UpdateTotal, только возвращать он должен void конечно.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>В случае же хелпера в после подходит UpdateTotal, только возвращать он должен void конечно.
Вот как раз я и хотел обратить внимание на то, что лучше бы этот метод ничего сам не менял, потому что это более универсальное решение. К примеру, сумму может быть нужно рассчитать предварительно, без изменения персистентного класса.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Вот как раз я и хотел обратить внимание на то, что лучше бы этот метод ничего сам не менял, потому что это более универсальное решение. К примеру, сумму может быть нужно рассчитать предварительно, без изменения персистентного класса.
Универсальное решение — это очень и очень хорошо, но часные случаи, все же имею правно на существование.
Так что оба решения, могут существовать, но выбор правильного решения можно сделать только в определенном контексте.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Универсальное решение — это очень и очень хорошо, но часные случаи, все же имею правно на существование.
Имеют, если дают какое то существенное преимущество. Здесь же я никаких преимуществ не вижу, зато недостатки ввиде скрытого изменения состояния в наличии.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Имеют, если дают какое то существенное преимущество. Здесь же я никаких преимуществ не вижу, зато недостатки ввиде скрытого изменения состояния в наличии.
Интересно, где вы видите скрытое изенение состояни?
В сигнатуре метода четко все прописано:
CRA>// ...
CRA>// Может быть Хелпером либо Иньекцией
CRA>//...
CRA>
Это очень хорошо, но тема "Куда же деть саму функцию (а не реализацию алгоритма)" не раскрыта
Вернее это пример того за что "сражается" stump: функция находится в самом классе (а реализация где-то еще).
Здравствуйте, AndrewVK, Вы писали:
AVK>Тем не менее преимуществ у такого варианта я так и не увидел.
Странно Андрей, как можно делать такие вывод в отрыве от контекста проблемы.
Давайте попробуем разобрать преимущества и недостатки обоих методов, для того, что бы не переругаться и найти компромисс:
Универсальное решение:
Позволяет клиенту полностью контролировать изменение Total, но в тоже время нагружает клиента лишней ответственностью.
Думаю тут спорить нет смысла, так как это общеизвестная зависимость
Частный случай:
Абстрагирует клиента, и избавляет его от ответственности (лишней или нет, зависти от задачи), при этом не дает возможность контролировать процесс изменения Total.
Итог:
В зависимости от задачи, мы можем принять решение:
Либо разрешаем клиенту вмешиваться в процесс изменения Total, добавляя ему доп. забот, либо упрощаем клиенту работу, используя абстракцию.
Конкретное, решение я еще раз повторю, можно принять только в конкретной ситуации, баталии на примере сферического коня в вакууме, смысла особого не имеют, я думаю, все со мной согласятся.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Странно Андрей, как можно делать такие вывод в отрыве от контекста проблемы.
ИМХО контекста тут предостаточно, чтобы ответить на заданный мной вопрос.
CRA>Универсальное решение: CRA>Позволяет клиенту полностью контролировать изменение Total, но в тоже время нагружает клиента лишней ответственностью.
Какой ответственностью? Присвоением значения свойству? Не нагружает оно, наоборот — эта ответственность переносится в тот кусок кода, который предназначен для изменения заказа, а не нагружает метод для вычисления суммы еще и ответственностью за присвоение значения. Тот самый затюканый здесь SRP на уровне метода.
Если же нам нужно абстрагировать клиента от процесса изменений (заметь, до этого нигде о необходимости подобной абстракции никто не говорил), нужно заводить отдельный метод — Update, который, в свою очередь, будет вызывать метод Calc. Главное — метод Calc не должен ничего сам менять.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
A>Это очень хорошо, но тема "Куда же деть саму функцию (а не реализацию алгоритма)" не раскрыта
Да ладно, не раскрыта
В конкретном примере "Order", после поста Stump от 23.07.08 15:20
Все стало прозрачно (для меня точно):
Заказ может содержать свойство Total, так как данное свойство находится в зоне его ответственности, НО заказ не может содержать метод расчета Total, так как это уже совершенно другая ответственность. Поэтому смысл применения правила, которое озвучил IB, в этом примере отсутствует, так как принцип SRP не позволяет нам «нагрузить» заказ и ответственность за содержание OrderLine и добавить ответственность за выполнение расчетов над OrderLine, думаю, что и IB и Stump со мной согласятся.
CRA>НО заказ не может содержать метод расчета Total, так как это уже совершенно другая ответственность.
Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм).
A>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм).
Т.е. юзер класса может даже не знать, что для подсчета Total используется некий другой класс, а не сам Order.
Здравствуйте, AndrewVK, Вы писали:
AVK>ИМХО контекста тут предостаточно, чтобы ответить на заданный мной вопрос.
Видимо, Вы обладаете очень интересным скилом – телепатией
Но, возможно вы могли упустить из вида, одно маленькое требование в одном из контекстов, которое звучит так:
Клиент не имеет право изменять Total напрямую, а может использовать только специально предоставленные ему механизмы.
AVK>Какой ответственностью? Присвоением значения свойству? Не нагружает оно, наоборот — эта ответственность переносится в тот кусок кода, который предназначен для изменения заказа,
Андрей, вы пытаетесь выполнить подмену понятий:
Пытаетесь уменьшить значимость сказанных мною слов, приравнивая их к совершенно другому понятию.
В данном конкретном случае, вы сделали так: ответственность == присвоение значения свойству, но это не так, ответственность есть ответственность, присвоение значения свойству – есть то как эта ответственность реализуется в коде.
AVK>а не нагружает метод для вычисления суммы еще и ответственностью за присвоение значения. Тот самый затюканый здесь SRP на уровне метода.
Применять SRP на уровне метода — это очень и очень проблематично, так как можно скатиться до применения SRP для каждого оператора, так что считаю обсуждение SRP для каждого метода бредовой идеей, и вы я думаю со мой согласитесь (ведь не будете же вы утверждать, что обработка исключений и вызов метода Х в методе Y нарушает SRP для метода Y)
AVK>Если же нам нужно абстрагировать клиента от процесса изменений (заметь, до этого нигде о необходимости подобной абстракции никто не говорил)
Видимо вы додумали задачу, до конца сделав все возможные предположения за меня. Я же пытаюсь сообщить вам, что конкретные необходимости не известны даже мне AVK>нужно заводить отдельный метод — Update, который, в свою очередь, будет вызывать метод Calc. Главное — метод Calc не должен ничего сам менять.
Я хочу обратить внимание, на один факт: Вы сделали предположение о том, что у меня написано в методе UpdateTotal, при этом предположение это вы сделали в удобном для себя разрезе.
Вы посчитали что в моем объекте нет метод Calc, я вам хочу сообщить, так как пример мой, то метод Calc там есть, и он сделан приватным, а вот почему он сделан приватным вы можете увидит в начале поста.
Здравствуйте, Aikin, Вы писали:
CRA>>НО заказ не может содержать метод расчета Total, так как это уже совершенно другая ответственность. A>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм).
Вы сейчас говорите об IoC, ничего в этом страшного не вижу, это стандартная практика:
public class Order
{
public Order(IOrderCalculator orderCalculator){}
public decimal GetTotal()
{
return orderCalculator.GetTotalFor(this);
}
}
Но хочу еще раз подчеркнуть, ответсвенность за расчет лежит на классе orderCalculator.
Здравствуйте, Aikin, Вы писали:
A>>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм). A>Т.е. юзер класса может даже не знать, что для подсчета Total используется некий другой класс, а не сам Order.
А как вы думали работает шаблон State
A>>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм). CRA>Вы сейчас говорите об IoC, ничего в этом страшного не вижу, это стандартная практика:
Я сейчас говорю про "Куда девать ф-ции [внешние] для класса". А точнее про то, что вы решили задачу, но это другая задача: "Куда девать алгоритм, внешний для класса".
A>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм).
Т.е. юзер класса может даже не знать, что для подсчета Total используется некий другой класс, а не сам Order.
P.S. Кроме того эта же задача может быть решена без IoC CRA>
CRA>public class Order
CRA>{
CRA> public decimal GetTotal()
CRA> {
CRA> returnOrderCalculatorHelper.GetTotalFor(this);
CRA> }
CRA>}
CRA>
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, Ага, Вы писали:
Z>Вы занимаетесь непонятной демагогией.
Z>stump привел пример предметной области: Re[9]: Куда девать ф-ции внешние для класса
, то GetTotal вполне может быть стратегией возвращающей Total, а Order может и не содержать свойство Total (в случае, если оно легко вычисляется, нет смысла его кешировать/обнавлять и т.д).
Подойдет в качестве мнения о предмете спора?
Z>Вы усложнили условия задачи:
хъ
Условия всегда меняются. А внешние для класса функции, которые находятся в классе обычно препятсвуют расширению функциональности.
A>>>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм). A>>Т.е. юзер класса может даже не знать, что для подсчета Total используется некий другой класс, а не сам Order. CRA>А как вы думали работает шаблон State
Ну, ... типа, ... ты ему опа, а он того... ит... считает
.
А Стэейт никакого отношения к вопросу не имеет, нету тут его и никаким клеем не приклеешь. Максимум Стратегию (а не IoC), да и то о ней клиент будет знать.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Здравствуйте, Aikin, Вы писали:
CRA>>>НО заказ не может содержать метод расчета Total, так как это уже совершенно другая ответственность. A>>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм). CRA>Вы сейчас говорите об IoC, ничего в этом страшного не вижу, это стандартная практика:
CRA>
CRA>public class Order
CRA>{
CRA> public Order(IOrderCalculator orderCalculator){}
CRA> public decimal GetTotal()
CRA> {
CRA> return orderCalculator.GetTotalFor(this);
CRA> }
CRA>}
CRA>
CRA>Но хочу еще раз подчеркнуть, ответсвенность за расчет лежит на классе orderCalculator.
Мне кажется в этом случае IOrderCalculator правильнее инжектить в методе GetTotal(), а заранее передавать его в конструкторе может быть лишним.
MC>Мне кажется в этом случае IOrderCalculator правильнее инжектить в методе GetTotal(), а заранее передавать его в конструкторе может быть лишним.
Не. В GetTotal() плохо.
Так как если пользователь которому нужен Total знает про нужную реализацию IOrderCalculator, то зачем ему метод GetTotal()?
A>Я сейчас говорю про "Куда девать ф-ции [внешние] для класса". А точнее про то, что вы решили задачу, но это другая задача: "Куда девать алгоритм, внешний для класса".
Серьезно?
Вот смотрите:
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal Total{get;set;}
}
public class OrderTotalCalculator
{
public decimal GetTotalFor(Order order){} //AndrewVK ;)
}
A>
A>>Не может, но может сам инициировать пересчет, т.е. фактически содержать метод расчета (но не алгоритм).
A>Т.е. юзер класса может даже не знать, что для подсчета Total используется некий другой класс, а не сам Order.
Вы путаете понятия:
1:
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal CalcTotalForAllLines(){}
}
2:
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal Total{get;set;}
}
В 1 случае, для класс Order сущестыует 2 ответственности:
1) Хранение заказа (строк заказа, заказчика и т.д.)
2) Выполнение операций расчета (будь то сумма по заказам, сумма НДС по строкам, сумма чистой прибыли по заказу и т.д.)
А вот во 2-м случае, только ответственность за хранение аттрибутов заказа.
A>P.S. Кроме того эта же задача может быть решена без IoC
Решение, которое тоже может быть правильным в определенном контексте, с этим не поспоришь
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Но, возможно вы могли упустить из вида, одно маленькое требование в одном из контекстов, которое звучит так: CRA>Клиент не имеет право изменять Total напрямую, а может использовать только специально предоставленные ему механизмы.
Это пожалуйста. Только Calc все равно должен быть отдельно и ничего не менять.
CRA>Пытаетесь уменьшить значимость сказанных мною слов, приравнивая их к совершенно другому понятию.
Попробуй перечитать что я пишу. Я ничего не пытаюсь преуменьшить.
CRA>ответственность есть ответственность, присвоение значения свойству – есть то как эта ответственность реализуется в коде.
Еще раз — есть две задачи: вычислить значение и изменить состояние персистентного объекта. Это разные задачи, объединение их в одном флаконе есть классическое нарушение SRP. Как следствие — та же невозможность просто посчитать значение, не меняя состояния. Совершенно классическая ситуация, какое тут еще тебе контекст нужен?
CRA>Применять SRP на уровне метода — это очень и очень проблематично, так как можно скатиться до применения SRP для каждого оператора
Бессмысленно. У тебя обвязка будет больше самого кода. Я всегда говорю одно и то же — от включения головы никакие принципы не спасают.
CRA>, так что считаю обсуждение SRP для каждого метода бредовой идеей, и вы я думаю со мой согласитесь
Вот сам утверждаешь что контекст нужен, и тут же без какого либо контекста заявляешь что SRP при делении на методы это бредовая идея. Нет, не соглашусь. Перегруженный функционалом метод ничем не лучше перегруженного функционалом класса.
CRA>Видимо вы додумали задачу, до конца сделав все возможные предположения за меня
За тебя? Исходную задачу разве ты приводил?
CRA>. Я же пытаюсь сообщить вам, что конкретные необходимости не известны даже мне
Ну да, очень удобно — додумываем и меняем по ходу дела задачу так, чтобы оказаться правым.
CRA>Я хочу обратить внимание, на один факт: Вы сделали предположение о том, что у меня написано в методе UpdateTotal
ННичего я не делал. Я просто не уклоняюсь от исходной темы топика — там самая суть где вычисление должно быть. Так что, если у тебя там внутри хитрая структура, то тогда зря ты это все сюда написал.
CRA>Вы посчитали что в моем объекте нет метод Calc, я вам хочу сообщить, так как пример мой
Я об этом чуть выше писал.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
CRA>>public class Order
CRA>>{
CRA>> public Order(IOrderCalculator orderCalculator){}
CRA>> public decimal GetTotal()
CRA>> {
CRA>> return orderCalculator.GetTotalFor(this);
CRA>> }
CRA>>}
CRA>>
CRA>>Но хочу еще раз подчеркнуть, ответсвенность за расчет лежит на классе orderCalculator.
MC>Мне кажется в этом случае IOrderCalculator правильнее инжектить в методе GetTotal(), а заранее передавать его в конструкторе может быть лишним.
Тоже можно использовать в ряде случаем, как говориться сколько людей, столько и мнений
Здравствуйте, stump, Вы писали:
S>Как раз есть, это очень плохо.
Чем именно?
S> Полхо так же то что ты не можешь этого понят, зациклившись на ничем не подкрепленных правилах.
Мои правила подкреплены практикой, метриками студии, формальным определением связности и тем самым здравым смыслом, на который ты ссылаешься.
S>Class coupling сильно влияет на простоту поддержки.
Не сильно.
S> Цитата из MSDN : S>
S> Rule Description
S>This rule measures class coupling by counting the number of unique type references that a type or method contains.
S>Types and methods with a high degree of class coupling can be difficult to maintain. It is a good practice to have types and methods that exhibit low coupling and high cohesion.
S>Избегайте чрезмерной связанности классов!
Отлично. Теперь давай попробуем внимательно прочитать о чем тут речь.
Во первых, class Coupling — это не связность. Это именно связность между классами, а связность — это то, что я цитировал из википедии.
Далее, речь тут не о тупом и произвольном числе связей между классами, а о количестве связий, которые возникают когда один класс использует несколько других. То есть, к нашему случаю никак не относится. Советуют избегать именно такого сценария и ограничивать количество других классов, которые использует данный класс.
S>Высокая связанность классов (high degree of class coupling — подчаркиваю) делает затруднительной поддержку.
Ясен байт. только именно degree, а не точное число.
S>Так вот, использование "четкого принципа": "методы которые работают через публичный контракт класса должны быть вынесены за пределы этого класса", неизбежно и автоматически ведет к повышению class coupling и уменьшению кохессии.
Фантастика! Связь есть. Понимаешь, она физически есть, я не знаю как еще эту мысль до тебя донести.
Эта связь есть всегда, вне зависимости от того, где находится метод. Как только ты вынес метод наружу, эта связь стала учитываться. И это хорошо. Но от того, что эта связь не учитывается если помещен внутрь класса, она никуда не исчезает.
Про "уменьшение кохессии" мы поговорим ниже.
S>Оказывается имеет, прямое отношение см. выше.
Нет не имеет, см выше.
S>Мы обсуждаем конкретные метрики конкретного кода, давай не будем "заводить рака за угол".
Аргументы не нравятся? Сценарий тот же самый, DI переводит неявную связь, никак не учитываемую метрикой Class Coupling, в явную, которую студия с удовольствием считает. Повторяю тот же вопрос — DI увеличивает связность?
S>Проблема в том что ты не можешь понять основных принципов анализа метрик кода, и меня это удивляет.
Точно я?
S>Их надо оценивать интегрально.
Интегрально — это сравнивать одни единицы измерения с другими?
S> В данном случае метрики показали что введение хелпера практически ни на что не повлияло (не повлияло и на Maintainability Index)
Прости, у тебя созрением все в порядке? =)
S>Можно провести дальнейший анализ, а почему Maintainability Index остался на месте
Лучше проведи дальнейший анализ, почему Maintainability Index уменьшился, несмотря на "катастрофическое" увеличение счетчика связей между классами.
S>Но. пример показал тенденцию. И ты этого не хочешь замечать, видимо по тому что эта тенденция тебе не нравится.
Я не хочу замечать тенденцию?! Да ты здесь вообще прямой подтасовкой цифр занимаешься.
S>Даже если онии хорошие, излишнее количество этих связей рано или поздно начнет создавать проблемы.
Я предпочту разбираться с проблемами явных связей, чем неявных зависимостей. И не только я, к слову, студия придерживается того же мнения.
S>Ага теперь про твои любимые "неявные связи". Вся прогрессивная общественность называет это кохессией (зацеплением).
Вся прогрессивная общественность называет это "связностью" (Coupling), прочитай еще раз внимательно википедию, особенно раздел про типы связности. Именно это называется связностью. Связность — физическая характеристика, при желании ее можно посчитать. (Чем, к слову, изанимается студия, считая maintainability index)
S>
S>Cohesion is a measure of how strongly-related or focused the responsibilities of a single class are. In object-oriented programming, if the methods that serve the given class tend to be similar in many aspects the class is said to have high cohesion. In a highly-cohesive system, code readability and the likelihood of reuse is increased, while complexity is kept manageable.
Твоя же любимая кохессия — это чисто логическая, умозрительная характеристика, "вот мне кажется, что у этого класса такая-то ответственность...", никакими метриками ты это не померяешь.
S>Сужествуют различные типы кохессии. Рассматриваемый нами тип (метод использует данные класса) можно отнести к комуникативной и частично функциональной кохессии S>
S>Communicational cohesion
S>Communicational cohesion is when parts of a module are grouped because they operate on the same data (e.g. a module which operates on the same record of information).
S>Functional cohesion (best)
S>Functional cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module (e.g. parsing XML in the case of Expat (XML)).
S>...
S>communicational and sequential cohesion are very good; and functional cohesion is superior.
S>(все цитаты из википедии)
Отлично. Теперь я беру все методы типа foo(), которые работают с A через публичный контракт, выношу их в отдельный класс и, для пущей красоты, обучаю их работать с интерфейсом IA. В итоге получаю отличнейший пример функциональной кохессии — superior!
ЧТД.
S>Т.е. размещение методов котоорые оперируют данными класса внутри самого класса имеет смысл и может дать положительный эффект.
Угу, только вынос всех методов в отдельный класс и группировка по функциональному принципу, за что я ратую, даст гораздо более глубокий положительный эффект.
S>Очень натянутые выводы, плохой анализ.
выводы очевидные, анализ отличный. Другое дело, что он тебе не нравится и ты изделие номер два, на глобус натягиваешь, но тут уж я ничего поделать не могу..
S> Любой видит, что с выносом методов в хелпер Maintainability index увеличился на 1% и за это заплачено значительным увеличением class coupling.
Только проблема в том, что Class Coupling косвенная характеристика, а Maintainability Index — итоговая, учитывающая, в том числе и Class Coupling. Учесть этот факт тебе видимо помешала врожденная способность к афигительному анализу..
S>Я не утверждаю, что есть "четкий принцип" — "если метод использует публичный контракт класса — он должен быть вынесен в другой класс."
А я утверждаю.
S>Я утверждаю, что есть оба принципа, и применительно к конкретным условиям может быть предпочтителен один или другой.
Раньше ты утверждал, что есть некий другой, не менее четкий принцип, который позволит однозначно определить — вносить метод в класс или нет. Но тайны так и не раскрыл.
S>А вот тупое (в смысле прямолинейное и бездумное) следование доному из них, ни к чему хорошему привести не может.
Я призываю к другому. Я призываю таки подумать и следовать тому принципу который я изложил.
Здравствуйте, adontz, Вы писали:
A>Иван, у тебя получается достаточно странная логика.
Логика у меня очень конкретная и последовательная.
A>вдруг начинаются прыжки в сторону на тему "для эффективной реализации, метод должен быть членом класса".
Это не "прыжек в сторону". Андрюша уже писал об этом, как, впрочем, и я. Принцип сформулирован очень четко. Если метод использует только публичный контракт для работы с классом, значит он должен находиться вне класса.
Уже использует, понимаешь? Это данность, его так задизайнили, не важно по каким причинам. Если по каким либо причинам методу нужно использовать приватные данные, не важно по каким, дизайн ли, производительность ли, еще что-то, то его место внутри.
Все очень четко, конкретно, понятно и прозрачно. В данном случае части методов, из соображений производительности, нужно иметь доступ к внутреннему состоянию, значит их место внутри класса.
A>У тебя получается некоторый String с методами, которые остались внутри просто потому что их нельзя выдернуть не просадив производительность (абсолютно нелепая выйдет компания) и помойка для всех остальных методов в хелпере.
С твоей точки зрения, общая помойка в одном классе лучше? Как раз классический пример "плохой кохессии" в терминологии stump-а. Не знаю, я по помойкам не специалист, я больше аккуратненькие клумбочки сервисов выращиваю..
Здравствуйте, MozgC, Вы писали:
MC>Нафига мне его как-то называть?
Ну, тебе же не нравится, что класс называется не string?
MC>И Причем тут экстраполировать, я тебе задал КОНКРЕТНЫЙ вопрос про КОНКРЕТНЫЕ проблемы с КОНКРЕТНЫМ классом.
Да не волнуйся так, я тебе очень конкретно ответил. Достаточно прочитать и немного подумать.
MC>Раз не можешь дать конкретный ответ,
Я тебе дал весьма конкретный ответ, с очень конкретным примером. Разница только в том, что класс не string называется, а проблемы ровно те же самые.
MC> Ты не способен адекватно и конкретно ответить на вопрос?
Боюсь, что ты просто не способен его задать..
A>Ну, ... типа, ... ты ему опа, а он того... ит... считает
No comments
A>А Стэейт никакого отношения к вопросу не имеет, нету тут его и никаким клеем не приклеешь.
Интересное заявление, как вам такое:
Изначально в требованиях к Order было:
В определенные дни заказы должны вести себя по разному:
в понедельник одно поведение,
во вторний другое,
во все остальные дни третье.
CRA>Вы путаете понятия:
Да действительно, прошу прощения.
Вот только не понятия, а неправильно прочитал исходное сообщение: Решил, что GetTotal лежит в самом классе.
Давайте вернемся и начнем сначала и исправим ошибку.
P.S. CRA>В 1 случае, для класс Order сущестыует 2 ответственности: CRA>1) Хранение заказа (строк заказа, заказчика и т.д.) CRA>2) Выполнение операций расчета (будь то сумма по заказам, сумма НДС по строкам, сумма чистой прибыли по заказу и т.д.)
Вы немного сгущаете краски в 2). Он не выполняет операции расчета, он знает кто может ему помочь их выполнить. В этом подходе не вижу ничего плохого
CRA>А вот во 2-м случае, только ответственность за хранение аттрибутов заказа.
Да, про анемичную модель мы слышали, но не прониклись Боюсь Order это не тот случай когда она хороша.
CRA>А вот Алгоритм расчета стоимости заказа, явно уже не отностится к отвественности абстракции "Заказ"
С этим тоже сложно поспорить, но: CRA>По этому получается такая вот загагулина:
В которой вы упустли несколько вариантов:
A>>А Стэйт никакого отношения к вопросу не имеет, нету тут его и никаким клеем не приклеешь. CRA>Интересное заявление, как вам такое: CRA>Изначально в требованиях к Order было: CRA>В определенные дни заказы должны вести себя по разному: CRA>в понедельник одно поведение, CRA>во вторний другое, CRA>во все остальные дни третье.
Ехххьььь, гори оно лесом: оффтоп, так оффтоп
Стэйт -- внутреннее состояние объекта, день недели -- состояние внешней среды!
От внешней среды может зависить Стратегия (подсчета чего-то там), но не внутреннее Состояние самого объекта.
Только из-за того что настал понедельник заказ не перейдет в состояние Заказан, вторник -- Оплачен, среда -- Отгружен, четверг -- Доставлен.
Здравствуйте, AndrewVK, Вы писали:
AVK>Это пожалуйста. Только Calc все равно должен быть отдельно и ничего не менять.
Ладно, пусть каждый останется при своем мнении
AVK>Попробуй перечитать что я пишу. Я ничего не пытаюсь преуменьшить.
Видимо ты этого даже не замечаешь
AVK>Еще раз — есть две задачи: вычислить значение и изменить состояние персистентного объекта. Это разные задачи, объединение их в одном флаконе есть классическое нарушение SRP. Как следствие — та же невозможность просто посчитать значение, не меняя состояния. Совершенно классическая ситуация, какое тут еще тебе контекст нужен?
Вы опять пытаетесь за меня написать метод UpdateTotal.
По вашим словам получается что клиет делающий так:
void Unknown()
{
decimal total = OrderHelper.CalaTotal(order); //получение значение
order.Total = total; //изменить состояние
}
Нарушает SRP, согласитесь это бред
AVK>Бессмысленно. У тебя обвязка будет больше самого кода. Я всегда говорю одно и то же — от включения головы никакие принципы не спасают.
О отлично, хоть в чем то мы согласны, а теперь посмотрите пожалуйста чуть выше
CRA>>, так что считаю обсуждение SRP для каждого метода бредовой идеей, и вы я думаю со мой согласитесь
AVK>Вот сам утверждаешь что контекст нужен, и тут же без какого либо контекста заявляешь что SRP при делении на методы это бредовая идея. Нет, не соглашусь. Перегруженный функционалом метод ничем не лучше перегруженного функционалом класса.
Длинну метода оставим на суд McConnell (Code Complete), но как бы Вы не хотели SRP для метода это жесть.
CRA>>Видимо вы додумали задачу, до конца сделав все возможные предположения за меня
AVK>За тебя? Исходную задачу разве ты приводил?
Вот именно, что не приводил, а вы попытались додумать, придумав то, что клиенту необходимы низкоуровневые операции по расчету Total, вы наверное будете говорить, что это не так, но вот вам цитата:
[23.07.08 18:43] К примеру, сумму может быть нужно рассчитать предварительно, без изменения персистентного класса.
AVK>Ну да, очень удобно — додумываем и меняем по ходу дела задачу так, чтобы оказаться правым.
Конечно, я додумываю задачу, так как я же ее точно не описал, а так как вы решили додумать ее за меня, мне же надо как то парировать (см. выше)
AVK>ННичего я не делал. Я просто не уклоняюсь от исходной темы топика — там самая суть где вычисление должно быть. Так что, если у тебя там внутри хитрая структура, то тогда зря ты это все сюда написал.
см. выше
AVK>Я об этом чуть выше писал.
см. выше
Здравствуйте, Aikin, Вы писали:
A>Да действительно, прошу прощения. A>Вот только не понятия, а неправильно прочитал исходное сообщение: Решил, что GetTotal лежит в самом классе. A>Давайте вернемся и начнем сначала и исправим ошибку.
Будем возвращаться потихоньку... A>P.S. A>Вы немного сгущаете краски в 2). Он не выполняет операции расчета, он знает кто может ему помочь их выполнить. В этом подходе не вижу ничего плохого
Стоп.
Если в даже в инетрфейста Order есть куча методов Calc*** — это это не гуд, даже если они используют state, strategy, etc.
CRA>>А вот во 2-м случае, только ответственность за хранение аттрибутов заказа. A>Да, про анемичную модель мы слышали, но не прониклись Боюсь Order это не тот случай когда она хороша.
На вкус и цвет...
Но посмотрите что может случиться:
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal CalcTotalForAllLines(){}
public decimal CalcTotalForEvenLines(){}
public decimal CalcTotalForRandomLines(){}
}
Вот такая может получить Rich model и в конечном итоге придется все Calc*** методы куда-нить переносить
Здравствуйте, C...R...a...S...H, Вы писали:
AVK>>Попробуй перечитать что я пишу. Я ничего не пытаюсь преуменьшить. CRA>Видимо ты этого даже не замечаешь
Судя по оценкам — не только я.
CRA>Вы опять пытаетесь за меня написать метод UpdateTotal.
Я тебе попытаюсь пояснить ситуацию в последний раз. В контексте топика речь шла именно о методе вычисления, нигде в исходном примере никаких изменений не было. Глядя на твое решение, я логично предположил, что, если у тебя нигде собственно вычислений не заявленно, значит они внутри Update. Если же у тебя там целая инфраструктура — то весь твой пример идет в лес и обсуждать я его не хочу, потому что на обсуждаемый здесь вопрос он никак не отвечает, самое главное осталось за кадром.
CRA>Вот именно, что не приводил
. Я до сих пор речь веду о нем. Естественно, твои умопостроения, которые ты по ходу разговора додумываешь, я не учитывал. Но твоя позиция абсолютно неконструктивна — всегда ведь можно сказать, на любое утверждение, что оно неверно, потому что у нас за кадром целое стадо коров спрятано. А конструктивно — это когда говорится что, а, главное, почему надо делать.
CRA>, а вы попытались додумать
То есть ты признаешь, что ты написал сообение, которое заведомо не проясняет ситуацию с обсуждаемым здесь вопросом, так? Тогда зачем ты его написал?
AVK>>Ну да, очень удобно — додумываем и меняем по ходу дела задачу так, чтобы оказаться правым. CRA>Конечно, я додумываю задачу, так как я же ее точно не описал
давай тогда и я додумаю, а то несправедливо получается: исходная задача не твоя и не моя, ты додумываешь, а мне нельзя. А вдруг унутре Update так:
var total = order.GetTotal();
order.Total = total;
Поэтому давай не будем тут сочинять по ходу разговора, а просто демонстрировать максимально ясно и понятно ту мысль, которую хочется продемонстрировать, и тогда все у нас получится.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
CRA>По вашим словам получается что клиет делающий так: CRA>
CRA>void Unknown()
CRA>{
CRA> decimal total = OrderHelper.CalaTotal(order); //получение значение
CRA> order.Total = total; //изменить состояние
CRA>}
CRA>
CRA>Нарушает SRP, согласитесь это бред
Несогласен! Никакой это не бред.
Он действительно нарушает SRP в случае, если изменить состояние order это не единственная его задача. Т.о. если этот код попадает в контроллер UI, например, мы получаем нарушение SRP: следить за Total никак не входит в его обязанность.
Если же это его единственная задача, то возникает вопрос: зачем нам третий ( ) класс, у которого все обязанностей -- обновить Total у order.
P.S. Вспомнился анекдот:
Утро, паковая зона. Работают два мужика: первый копает яму, второй закапывает. Подходит к ним прохожий:
-- Что вы делаете? Зачем один копает яму, а второй закапывает? Это ведь глупо
-- Ничего не глупо. Нас всего трое. Второй должен садить деревья. Он заболел.
Смех смехом, но при увеличении количества классов уменьшается надежность системы.
Здравствуйте, Aikin, Вы писали:
CRA>>По этому получается такая вот загагулина: A>В которой вы упустли несколько вариантов:
A>И это ничем не хужу гуда: A>
A>public class Order
A>{
A> public decimal GetTotal()
A> {
A> return OrderCalculator.GetTotalFor(this);
A> }
A>}
A>
Любой вариант может существовать, кому то нравиться даже так:
public decimal GetTotal(IOrderCalculator calc)
{
return calc.GetTotalFor(this);
}
Проблема не в реализации, а в концепции.
Повторюсь:
Свойство Total может существовать в Order (оно может наже наззывать GetTotal), но фукции аля CalcTotal etc в Order не место.
Пусть даже GetTotal==CalcTotal но концептуально CalcTotal — это явно говорит пользователю о том, что CalcTotal, это спец функция для расчета total, а вот GetTotal пользователю сообщает только, то, что свойство Total ReadOnly
A>P.S. Надоело как-то выкать, может на "ты" перейдем?
ОК
Здравствуйте, Aikin, Вы писали:
A>Стэйт -- внутреннее состояние объекта, день недели -- состояние внешней среды! A>От внешней среды может зависить Стратегия (подсчета чего-то там), но не внутреннее Состояние самого объекта. A>Только из-за того что настал понедельник заказ не перейдет в состояние Заказан, вторник -- Оплачен, среда -- Отгружен, четверг -- Доставлен.
Я тут допустил оплошность, переформулирую требование В определенные дни создания заказа, но должн вести себя по разному
Хотя странно почему ты, считешь что заказ не может перейти в определенное состояние из-за даты, а как же состояние Просрочен
A>>Вы немного сгущаете краски в 2). Он не выполняет операции расчета, он знает кто может ему помочь их выполнить. В этом подходе не вижу ничего плохого CRA>Стоп. CRA>Если в даже в инетрфейста Order есть куча методов Calc*** — это это не гуд, даже если они используют state, strategy, etc.
Боже упаси. С чего вы это взяли? Я первый оторву руки программисту/архитектору предложившему это. В Order есть всего один метод-геттер GetTotal() (get_Total()). Не CalculateTotal(), а именно GetTotal()
CRA>>>А вот во 2-м случае, только ответственность за хранение аттрибутов заказа. A>>Да, про анемичную модель мы слышали, но не прониклись Боюсь Order это не тот случай когда она хороша. CRA>На вкус и цвет... CRA>Но посмотрите что может случиться:
CRA>Вот такая может получить Rich model и в конечном итоге придется все Calc*** методы куда-нить переносить
Не, это не рич модель, а страшний сон архитектора.
Рич модель будет такая: CRA>
CRA>public class Order
CRA>{
CRA> public IList<OrederLine> OrderLines {get;set;} // У меня есть вопросы по публичной коллекции, но для простоты пусть будет так
CRA> public decimal Total{get;}
CRA>}
CRA>
CRA>переформулирую требование В определенные дни создания заказа, но должн вести себя по разному
Таки Cтратегия
CRA>Хотя странно почему ты, считешь что заказ не может перейти в определенное состояние из-за даты, а как же состояние Просрочен
Да, тут есть доля правды. Но дело в том, что не сам объект должен перевевести себя в состояние Просрочен, а внешняя к нему функциональность.
...Хм, хотя в состояние Невалиден он может перевести себя сам... Но, с другой стороны, перевод этот осуществляется после вызова Validate()...
Скользко, очень скользко. Хорошо хоть у нас не эта задача сейчас стоит, а вычисление стоимости.
CRA>Любой вариант может существовать, кому то нравиться даже так: CRA>Свойство Total может существовать в Order (оно может наже наззывать GetTotal), но фукции аля CalcTotal etc в Order не место. CRA>Пусть даже GetTotal==CalcTotal но концептуально CalcTotal — это явно говорит пользователю о том, что CalcTotal, это спец функция для расчета total, а вот GetTotal пользователю сообщает только, то, что свойство Total ReadOnly
Полностью с этим согласен и даже отстаивал эту позицию, но спор начался из-за того что не сошлись во мнении куда помещать метод GetTotal(): в сам класс или в Хелпер
CRA>>Вы опять пытаетесь за меня написать метод UpdateTotal.
AVK>Я тебе попытаюсь пояснить ситуацию в последний раз. В контексте топика речь шла именно о методе вычисления, нигде в исходном примере никаких изменений не было. Глядя на твое решение, я логично предположил, что, если у тебя нигде собственно вычислений не заявленно, значит они внутри Update. Если же у тебя там целая инфраструктура — то весь твой пример идет в лес и обсуждать я его не хочу, потому что на обсуждаемый здесь вопрос он никак не отвечает, самое главное осталось за кадром.
Да, метод вычисления находится в Update, как это меняет ситуацию?
Предвосхищая ваши заявления, SRP для каждого метода это бред, так как вы перестали поддерживать дискуссию по этому вопросу, думаю что вы со мной согласны.
. Я до сих пор речь веду о нем. Естественно, твои умопостроения, которые ты по ходу разговора додумываешь, я не учитывал. Но твоя позиция абсолютно неконструктивна — всегда ведь можно сказать, на любое утверждение, что оно неверно, потому что у нас за кадром целое стадо коров спрятано. А конструктивно — это когда говорится что, а, главное, почему надо делать.
Видимо пытаясь бороться с методом UpdateTotal вы совершенно забыли мой пример, я специально для вас его повторю (изменив метод Update на Calc)
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal Total{get;set;}
}
public class OrderTotalCalculator
{
public decimal CalcTotal(Order order){}
}
AVK>То есть ты признаешь, что ты написал сообение, которое заведомо не проясняет ситуацию с обсуждаемым здесь вопросом, так? Тогда зачем ты его написал?
Посмотрите на мой пост от 23.07.08 17:39 (С него и началось наше с Вами обсуждение), я в нем предлагаю способ и полностью его обосновываю, то что вы предлажили назвать метод не UpdateTotal, а CalcTotal ситуации не меняет, а сейчас вы пытаетесь вообще мое решение засудить.
AVK>давай тогда и я додумаю, а то несправедливо получается: исходная задача не твоя и не моя, ты додумываешь, а мне нельзя. А вдруг унутре Update так: AVK>
AVK>var total = order.GetTotal();
AVK>order.Total = total;
AVK>
AVK> AVK>Поэтому давай не будем тут сочинять по ходу разговора, а просто демонстрировать максимально ясно и понятно ту мысль, которую хочется продемонстрировать, и тогда все у нас получится.
В моем примере я имел ввиду следующее
Что то вы перегибаете палку.
Вы хотите что бы я полностью написал метод UpdateTotal, но это бред.
Обговорю^ в UpdateTotal ведется расчет Total и оно записывается в Order.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Да, метод вычисления находится в Update, как это меняет ситуацию?
Возвращаемся к началу топика и читаем по новой, на этот вопрос я уже отвечал.
CRA>Предвосхищая ваши заявления, SRP для каждого метода это бред
Я тоже так умею. Предвосхищая твои заявления — не применять SRP для методов это бред.
CRA>Вы хотите что бы я полностью написал метод UpdateTotal, но это бред.
Нет, я хочу чтобы в твоем примере не было вещей, важных для обсуждаемого вопроса, которые по ходу обсуждения начинают всплывать.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
А мне кажется вы оба не правы. Если подсчет Total выносить из класса Order, то в нем не должно остаться ни Total, ни CalcTotal ни других упоминаний о том, что у него есть Total. Это уже ответственность OrderTotalCalculator.
Здравствуйте, IB, Вы писали:
IB>Это не "прыжек в сторону". Андрюша уже писал об этом, как, впрочем, и я. Принцип сформулирован очень четко. Если метод использует только публичный контракт для работы с классом, значит он должен находиться вне класса.
Никакое усердно использование правила не заменит усердное использование мозга. Правило — это всего лишь правило и его повсеместное необдуманное применение принесёт больше вреда чем пользы, как и использование любого другого правила. Об чём я ниже и напишу.
IB>Уже использует, понимаешь? Это данность, его так задизайнили, не важно по каким причинам. Если по каким либо причинам методу нужно использовать приватные данные, не важно по каким, дизайн ли, производительность ли, еще что-то, то его место внутри. IB>Все очень четко, конкретно, понятно и прозрачно. В данном случае части методов, из соображений производительности, нужно иметь доступ к внутреннему состоянию, значит их место внутри класса.
Ага, теперь давай рассмотрим последствия. У тебя внешний интерфейс библиотеки — контракт для общения с другими компонентами, построен не на основе понятий предметной области, здравого смысла, и т.п., а на основе исключительно деталей реализации. Причём это поведение, о чудо, вызванно побуждениями к повешению инкапсуляции! Всё же специалист твоего уровня должен бы уметь видеть не только отдельные кирпичи но и стену в целом. Понизить инкапсуляцию внутри библиотеки, но предоставить наружу интерфейс не зависящирй от деталей реализации задача более приоритетная.
То есть у тебя если вдруг станет ясно, что метод можно реализовать только за счёт публичного контракта, то он прыгнет в хелпер. Если выясниться что метод можно более эффективно реализовать за счёт обращения к внутренним полям класса, то он прыгнет в класс. А когда смотришь на всё это снаружи, не углубляясь в детали реализации, то не наблюдаешь абсолютно никакой системы (что не удивительно), сплошной бардак. Метод LeftSubstring() может быть в строке, а метод RightSubstring() в хелпере. Потому что правило! Твоя цель — логический бардак снаружи, за счёт только тебе, как автору, видной внутренней красоты?
IB>С твоей точки зрения, общая помойка в одном классе лучше? Как раз классический пример "плохой кохессии" в терминологии stump-а. Не знаю, я по помойкам не специалист, я больше аккуратненькие клумбочки сервисов выращиваю..
Ты не специалист? Не верю! Неужели никогда на Си++ не писал? Там этот принцип доведён до точки, STL называется. Данные отдельно, алгоритмы-манипуляторы-хелперы отдельно.
Что мы имеем? Вот я хочу найти элемент в сортированном массиве. Ищу find, search, index_of и естественно ничего не нахожу. Есть binary_search, но он проверят наличие элемента не возвращая его индекса. Автор библиотеки (привет тебе автор!) называл функцию lower_bound. Слабо угадать такое название? Оно абсолютно никак не связано с классом vector.
Хелперы имеют один большой недостаток следующий из размазанности функционала по классам — необходимо помнить (sic!) или постоянно искать в документации названия классов для выполнения операций с данным классом. Даже при всём уважении к MSDN, не надо лукавить. Необходимо помнить, искать бесполезно. Не надо впаривать что это будут классы StringCase и типа набери string, ctrl+space и будет счастье. Не будет, у нас нет StringCase, есть CultureInfo. Это что, можно угадать или быстро найти, что для перевода строки в верхний регистр надо использовать класс CultureInfo?!
Интерфейс подобной библиотеки становиться полным кошмаром, его надо держать постоянно в голове, помня семантические связи между классами. Почему ToUpper не просто внесли в string, а продублировали там? Подублировали, функция уже была в хелпере! Да потому что не на правила полагались, а хорошенько подумали головой и не об абстрактной крутости подсчитаной нарошной утилиткой, а о людях, живых людях, которым надо будет каждый день пользоваться классом String и которые не хотят что-то помнить и лазать по документации, а хотят нажать точку и увидеть список того, что можно с этим стрингом произвести. Юзабилити оно бывает не только в интерфейсах. Тебе может шашечки, рейтинги связности считать, другой абстракционистикой маятся, а народу ехать, сейчас и с комфортом. Рейтинги и правила это, конечно, познавательно, но голову никто не отменял.
Более того, пока всё прогрессивное сообщество развивается в одну сторону, ты умудряешься развиваться в прямо противоположную. Зачем по твоему придумали extension methods? Они по твоей логике вообще не должны существовать, ведь они не могут обращаться к внутренним полям класса, а значит должны быть вынесены в хелпер. Но нет, придумали специальный синтаксис, расширили язык именно для удобного использования методов. Чтобы нажать точку и выплыли методы. Вот на таком примитивном уровне мотивации всё и зиждется. Как видишь, никаких рейтингов связности.
Казалось бы вот оно счастье, писать хелперы как extension methods, но суть как раз в другом. Открой MSDN и прочитай "Implementers of class libraries should not use extension methods". Суть в том, чтобы иметь возможность расширять классы от которых нельзя наследоваться и иметь возможность удобно использовать эти расширения. Ты не можешь вставить класс в иерархии между IEnumerable<T> и List<T>, не можешь добавить метод в string. Не можешь, но хочешь. И вот тебе, пожалуйста extension methods. Удобство использования ставиться во главу. Linq от которого все тащаться это же чистое удобство. Всё что там есть замечательно реализуется и без extension methods, но кто этим будет пользоваться в таком виде?
Здравствуйте, stump, Вы писали:
S>Хочется, конечно подиспутировать по существу, но совершенно надоело терпеть твое хамство.
У тебя восхитительная особенность, при недостатке аргументов обвинять оппонента в некорректности ведения дискуссии. И ты меня после этого в хамстве обвиняешь? Ты вообще внимательно читал, что сам написал?
Здравствуйте, adontz, Вы писали:
A>Что мы имеем? Вот я хочу найти элемент в сортированном массиве. Ищу find, search, index_of и естественно ничего не нахожу. Есть binary_search, но он проверят наличие элемента не возвращая его индекса. Автор библиотеки (привет тебе автор!) называл функцию lower_bound. Слабо угадать такое название? Оно абсолютно никак не связано с классом vector.
A>Хелперы имеют один большой недостаток следующий из размазанности функционала по классам — необходимо помнить (sic!) или постоянно искать в документации названия классов для выполнения операций с данным классом. Даже при всём уважении к MSDN, не надо лукавить. Необходимо помнить, искать бесполезно. Не надо впаривать что это будут классы StringCase и типа набери string, ctrl+space и будет счастье. Не будет, у нас нет StringCase, есть CultureInfo. Это что, можно угадать или быстро найти, что для перевода строки в верхний регистр надо использовать класс CultureInfo?!
Рома, с этой ужасной проблемой прекрасно справляются extension методы.
A> Зачем по твоему придумали extension methods? Они по твоей логике вообще не должны существовать
Ты, видимо, читал что то свое. Я не припоминаю чтбы Иван говорил что они не нужны. Как мне помнится, он говорил обратное. Так что неясно, с кем ты споришь.
A> Открой MSDN и прочитай "Implementers of class libraries should not use extension methods".
Этот приём называется "художественная резьба по цитатам". Вот полная цитата:
Implementers of class libraries should not use extension methods to avoid creating new versions of assemblies.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
A>> Открой MSDN и прочитай "Implementers of class libraries should not use extension methods". AVK>Этот приём называется "художественная резьба по цитатам". Вот полная цитата: AVK>
Implementers of class libraries should not use extension methods to avoid creating new versions of assemblies.
И в чём принципиально поменялся смысл? Extension methods в библиотеках запрещены и служат лишь для удобного расширения чужих библиотек. Свои собственные библиотекии не расшииряют через extension methods. Ты не прыгай несущественной части сообщения, выдёргивая самое, на твой взгляд, сомнительное, ты отвечай на сущесвенную часть. Я тебе подскажу, существенная часть, это например: "Метод LeftSubstring() может быть в строке, а метод RightSubstring() в хелпере".
Здравствуйте, adontz, Вы писали:
AVK>>Этот приём называется "художественная резьба по цитатам". Вот полная цитата: AVK>>
Implementers of class libraries should not use extension methods to avoid creating new versions of assemblies.
A>И в чём принципиально поменялся смысл?
В том, что следует не всегда избегать использования extension методов в библиотеке, а с одной, вполне конкретной целью.
A> Extension methods в библиотеках запрещены
В цитате этого нет. В цитате сказано что следует избегать использования extension методов с целью не создавать новую версию сборки. С другими целями использовать можно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, Aikin, Вы писали:
Z>А мне кажется вы оба не правы. Если подсчет Total выносить из класса Order, то в нем не должно остаться ни Total, ни CalcTotal ни других упоминаний о том, что у него есть Total. Это уже ответственность OrderTotalCalculator.
Нет так не пойдет. В первоначальном моем примере был только код, демонстрирующий два подхода к размещению методов с логикой класса, и ничего о предметной области. Но, как я вижу примерчик оказался необычайно жизненным
С точки зрения предметной области в нем был один существенный изъян. Если посмотреть на формулу расчета суммы ордера, то мы заметим что там присутствует ставка налога из класса Product. Представте, что у нас есть тысяча ордеров (наверное часть из них оплачена), и в один прекрасный момент мы изменяем ставку налога в Product (Дума так решила...). Если после этого мы начнем сверять суммы по ордерам с суммами по платежам, то обнаружим что суммы теперь "не бьют". Зато за дверями стоят юзера намереньем побить уже нас.
Поэтому, с точки зрения здравого смысла (который у некоторых не в почете) сумму ордера конечно же надо хранить.
Ага это сразу заметил , но тогда я уклонился от дискусии, потому что она уводила в сторону от первоначальной цели. Но дискуссия таки все равно поперла.
С другой строны в первоначальной постановке ничего не говорится о том, должен ли меняться способ расчета суммы или нет.
Давайте рассмотрим два крайних случая:
1. Способ расчета никогда не будет менятся, потому что мы пишем утилиту для разовой конвертации данных.
2. Может быть несколько способов расчета суммы, они могут меняться в течении жизненного цикла прогаммы (например завтра будет програссивная шкала налога).
Как это может повлиять на дизайн? Или никак не повлияет, руководствуемся "четким принципом" и все.
Большой +
Все эти аргументы и многие другие уже приводились здесь. Ты действительно хорошо пишешь, но сомневаюсь, что тебе удастся в чем то убедить Ивана и Андрея.
Здравствуйте, adontz, Вы писали:
A>Начиная с "However, many of the features that make extension methods so useful for library consumers can be problematic for class library authors".
In particular, there are 2 primary issues ...
... if an instance member is ever added to a type with the same name as an extension method, then the extension method can be rendered uncallable.
Во-первых это вранье, потому что остается возможность вызова в классическом виде. Во-вторых единственный способ добавить к типам готовой библиотеки метод экземпляря — сделать наследника. Однако, если прокастить наследника к базовому типу — extension метод можно звать и нужным способом. В-третьих если пользюк называет методы наследников библиотеки так же, как существующий метод в той же библиотеке, то он сам себе злобный буратин.
The second issue is that an extension method author cannot prevent other programmers from writing conflicting extensions.
А это вообще специально сделано, чтобы как раз таки я мог подменять один функционал другим на уровне статического полиморфизма. Например, заменять лямбды на expression tree.
Вобщем, слабенькая статейка, неубедительно.
A>В общем, в библиотеках от extension methods проблем больше, чем пользы.
Расскажи это МС, которые, вот идиоты, класс Enumerable в библиотеку воткнули.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Возвращаемся к началу топика и читаем по новой, на этот вопрос я уже отвечал. AVK>Я тоже так умею. Предвосхищая твои заявления — не применять SRP для методов это бред. AVK>Нет, я хочу чтобы в твоем примере не было вещей, важных для обсуждаемого вопроса, которые по ходу обсуждения начинают всплывать.
Признаюсь, вам все же удалось меня провести, а я стормозил из-за того что поторопился.
Смотрим внимательно:
Ваш комментарий к моему решению:
Первый пост:
CRA> public decimal UpdateTotal(Order order){}
Лучше все же CalcTotal,а не UpdateTotal.
Второй пост:
Вот как раз я и хотел обратить внимание на то, что лучше бы этот метод ничего сам не менял, потому что это более универсальное решение. К примеру, сумму может быть нужно рассчитать предварительно, без изменения персистентного класса.
А теперь вскрываем карты
После этих постов, что мол лучше функцию обозвать CalcTotal, а не UpdateTotal и заявлений про SRP для методов (которые Вы потом прекратили, из-за того, что это полный бред. И скрытое изменение состояния, которое я так же опроверг)
Вы выдаете вот такое сообщение:
В контексте топика речь шла именно о методе вычисления, нигде в исходном примере никаких изменений не было. Глядя на твое решение, я логично предположил, что, если у тебя нигде собственно вычислений не заявленно, значит они внутри Update. Если же у тебя там целая инфраструктура — то весь твой пример идет в лес и обсуждать я его не хочу, потому что на обсуждаемый здесь вопрос он никак не отвечает, самое главное осталось за кадром.
Получается интересная ситуация, либо Вы все забыли пока обсуждали переименование метода UpdateTotal в CalcTotal.
Либо Ваша любимая методика подмены понятий дает о себе знать.
Третьего уж простите не дано
Здравствуйте, stump, Вы писали:
S>Поэтому, с точки зрения здравого смысла (который у некоторых не в почете) сумму ордера конечно же надо хранить. S>Как это может повлиять на дизайн? Или никак не повлияет, руководствуемся "четким принципом" и все.
Мои решения вобщем-то не особо зависят от того, планируется ли менять систему подсчета в течении жизенного цикла программы
// вариант когда есть шансы подсчитывать разные ордеры по разномуpublic class Order1
{
decimal? total;
public decimal Total
{
get
{
if (total == null)
throw new InvalidOperationException();
return total.Value;
}
}
public void CalculateTotal(IOrderTotalCalculator calc)
{
calc.Calcuate(this);
}
}
// вариант когда таких шансов практически нет, подсчет тривиаленpublic class Order2
{
decimal? total;
public decimal Total
{
get
{
if (total == null)
total = CalculateTotal();
return total.Value;
}
}
private decimal CalculateTotal()
{
return 5;
}
}
// вариант когда таких шансов практически нет, подсчет нетривиаленpublic class Order3
{
IOrderTotalCalculator calc;
decimal? total;
public decimal Total
{
get
{
if (total == null)
total = calc.CalculateTotal(this);
return total.Value;
}
}
}
И наконец четвертый вариант — хранение Total отдельно в классе содержащем Order и его аггрегаты.
И все эти решения могут кардинально измениться в зависимости от множества неуказанных требований.
CRA>>void Unknown()
CRA>>{
CRA>> decimal total = OrderHelper.CalaTotal(order); //получение значение
CRA>> order.Total = total; //изменить состояние
CRA>>}
CRA>>
A>Несогласен! Никакой это не бред. A>Он действительно нарушает SRP в случае, если изменить состояние order это не единственная его задача. Т.о. если этот код попадает в контроллер UI, например, мы получаем нарушение SRP: следить за Total никак не входит в его обязанность. A>Если же это его единственная задача, то возникает вопрос: зачем нам третий ( ) класс, у которого все обязанностей -- обновить Total у order.
Посмотри на метод, который я специально не стал удалять.
Еще раз — есть две задачи: вычислить значение и изменить состояние персистентного объекта. Это разные задачи, объединение их в одном флаконе есть классическое нарушение SRP
Подскажи пожалуйста, как решить задачу перерасчета Total не нарушая твоего принципа SRP
Хочу обратить внимание, что Unknown — это метод.
Здравствуйте, AndrewVK, Вы писали:
A>>В общем, в библиотеках от extension methods проблем больше, чем пользы. AVK>Расскажи это МС, которые, вот идиоты, класс Enumerable в библиотеку воткнули.
Он не расширяет классы той же библиотеки. По существу (где именно существо я указал выше, напомню это про LeftSubstring и RightSubstring) тебе, видимо, сказать нечего и ты придираешься к мелочам. А жаль.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Получается интересная ситуация, либо Вы все забыли пока обсуждали переименование метода UpdateTotal в CalcTotal. CRA>Либо Ваша любимая методика подмены понятий дает о себе знать.
Ничего я не подменяю. Речь о неправильности изменений шла именно в контексте исходного примера. Т.е. я предполагал, что твой код является логическим продолжением того, исходного примера. Т.е. никаких хитрых внутренних методов Calc там нет, иначе бы было бы логично их сразу и продемонстрировать.
Я вижу, аргументы по делу уже вообще даже не упоминаются. Думаю, на сем стоит закончить.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, adontz, Вы писали:
A>(где именно существо я указал выше, напомню это про LeftSubstring и RightSubstring) тебе, видимо, сказать нечего и ты придираешься к мелочам. А жаль.
Это ты какой то левый пример придумал и теперь пытаешься втиснуть его в качестве аргумента.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
A>>(где именно существо я указал выше, напомню это про LeftSubstring и RightSubstring) тебе, видимо, сказать нечего и ты придираешься к мелочам. А жаль. AVK>Это ты какой то левый пример придумал и теперь пытаешься втиснуть его в качестве аргумента.
Андрей, Рома действительно написал большой пост, многое в нем действительно по делу, но ты самое главное, относящееся к теме дискуссии, оставил без комментариев, а начал писать замечания по мелочам.
Здравствуйте, MozgC, Вы писали:
MC>Андрей, Рома действительно написал большой пост, многое в нем действительно по делу, но ты самое главное, относящееся к теме дискуссии, оставил без комментариев, а начал писать замечания по мелочам.
Я ответил на то, что посчитал самым важным. Если тебе интересны мои комментарии к чему то конкретно — приводи цитаты, постараюсь ответить.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
A>>(где именно существо я указал выше, напомню это про LeftSubstring и RightSubstring) тебе, видимо, сказать нечего и ты придираешься к мелочам. А жаль. AVK>Это ты какой то левый пример придумал и теперь пытаешься втиснуть его в качестве аргумента.
Есть ты не видишь проблемы в том, что метод LeftSubstring описан в классе, так как для его эффективной реализации необходим доступ к приватным полям, а метод RightSubstring описал в хелпере, потому что его эффективная реализация не требует доступа к внутренним полям, то я тоже не вижу предмета для обсуждения с тобой. Как можно обсуждать с человеком то, что он не видит?!
Здравствуйте, adontz, Вы писали:
A>Есть ты не видишь проблемы в том, что метод LeftSubstring описан в классе, так как для его эффективной реализации необходим доступ к приватным полям, а метод RightSubstring описал в хелпере, потому что его эффективная реализация не требует доступа к внутренним полям, то я тоже не вижу предмета для обсуждения с тобой.
Нет, я не вижу, с какой это стати LeftSubstring требует доступа, а RightSubstring нет. Кроме того, по вопросам производительности и дизайна я в этом топике уже высказывался. Re[16]: Куда девать ф-ции внешние для класса
Здравствуйте, AndrewVK, Вы писали:
AVK>Ничего я не подменяю. Речь о неправильности изменений шла именно в контексте исходного примера. Т.е. я предполагал, что твой код является логическим продолжением того, исходного примера. Т.е. никаких хитрых внутренних методов Calc там нет, иначе бы было бы логично их сразу и продемонстрировать.
Продублирую свой первый пост (и добавлю комментарий):
public class Order
{
public IList<OrederLine> OrderLines {get;set;}
public decimal Total{get;set;}
}
///Может быть Хелпером либо Иньекциейpublic class OrderTotalCalculator
{
public decimal UpdateTotal(Order order)
{
//метод Calc тут
}
}
Из первых ваших постов я сделал вывод что, вам это понятно.
Даже Вы сами так написали
я логично предположил, что, если у тебя нигде собственно вычислений не заявленно, значит они внутри Update
Теперь я так понимаю, у Вас новая зацепка,
никаких хитрых внутренних методов Calc там нет
Похоже, и для этой ветки разговра у Вас не осталось аргументов, и как правильно заметил adontz Вы начинаете придираться к мелочам.
Понятно, аргументов по прежнему нет, попытка вывернуть все на изнанку наличествует. Продолжать мне не интересно. Будут вопросы по делу, а не очередные попытки поймать на какой нибудь ерунде — обращайтесь.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Нет, я не вижу, с какой это стати LeftSubstring требует доступа, а RightSubstring нет.
Например, LeftSubstring повторно использует тот же массив char[], просто указывая новую длину. RightSubstring создаёт новый.
По существу есть что сказать или "я не верю, что LeftSubstring может требовать доступ, а RightSubstring нет" это всё что ты можешь ответить?
Глубинную проблему, которую я пытался доходчиво проиллюстрировать, а именно, раскидывание по разным классами логически связанных, в том числе парных, методов при использовании правила "Если достаточно публичного контракта, метод переносится из основного класса в хелпер" ты не видишь?
Z>А мне кажется вы оба не правы. Z>Если подсчет Total выносить из класса Order, то в нем не должно остаться ни Total, ни CalcTotal ни других упоминаний о том, что у него есть Total. Это уже ответственность OrderTotalCalculator.
Ты будешь смеяться, но я считаю что правы все Каждый по своему и каждый в своем контексте
Давай от теории перейдем к практике: I Анемичная модель:
а) Класс Order нужен для бизнесс логики.
Бизнесс логика знает о том, кого нужно спросить, чтобы подсчитать Total. Total здесь действительно ни к чему:
public class Order
{
public IList<OrderLine> OrderLines {get;}
}
б) Класс Order нужен для показа пользователю (UI) и для сохрания в базе (DAO)
ДАО совершенно не должна знать про то где и как достать Total. C UI не так все категорично, но все же желательно, чтобы UI тоже не знал где брать Total (меньше знаешь -- крепче спишь ).
Поэтому в этом случае мы имеем следующую реализацию:
public class Order
{
public IList<OrderLine> OrderLines {get;}
public decimal Total {get; set;}
}
II Rich модель:
Скорее всего тут тоже будет несколько вариантов, но я покажу тот, что больше всего нравится мне (именно его я бы и использовал в своем приложении):
Класс Order сам ответственнен за свою целостность и согласованность. Он контролирует добавление и удаление строк заказа и инициирует пересчет стоимости при изменении.
public class Order
{
private readonly IList<OrderLine> _orderLines = new List<OrderLine>();
private decimal _total = 0M;
public IEnumerator<OrderLine> GetOrderLinesEnumerator()
{
return _orderLines.GetEnumerator();
}
public void AddOrderLine(OrderLine orderLine)
{
_orderLines.Add(orderLine);
updateTotal();
}
public void RemoveOrderLine(OrderLine orderLine)
{
_orderLines.Remove(orderLine);
updateTotal();
}
public decimal Total
{
get { return _total; }
}
private void updateTotal()
{
// тут может быть DI, ServiceLocator или статический хелпер как ниже
_total = OrderTotalCalculator.CalculateTotalFor(this);
}
}
Судить о достоинствах и недостатках каждого подхода предоставляю вам.
CRA>Посмотри на метод, который я специально не стал удалять. CRA>Хочу обратить внимание, что Unknown — это метод. Чей метод? Вот в чем вопрос
Вот именно об этом я и говорил:
Он действительно нарушает SRP в случае, если изменить состояние order это не единственная его задача. Т.о. если этот код попадает в контроллер UI, например, мы получаем нарушение SRP: следить за Total никак не входит в его обязанность.
Если же это его единственная задача, то возникает вопрос: зачем нам третий класс, у которого все обязанностей -- обновить Total у order.
Заметь, я нигде не говорю какой класс содержит этот метод. Я рассматриваю два возможных варианта: у класса нет больше обязаностей кроме как следить за Total для Order и у класса есть такие обязанности.
CRA>
CRA>Еще раз — есть две задачи: вычислить значение и изменить состояние персистентного объекта. Это разные задачи, объединение их в одном флаконе есть классическое нарушение SRP
CRA>Подскажи пожалуйста, как решить задачу перерасчета Total не нарушая твоего принципа SRP
Никакой он не мой. Он общий и в большинстве случаев интуитивно понятный.
Пример кода здесь: Re[25]: Куда девать ф-ции внешние для класса
Здравствуйте, stump, Вы писали:
S>Поэтому, с точки зрения здравого смысла (который у некоторых не в почете)
Кто бы это мог быть?
S>сумму ордера конечно же надо хранить.
С точки зрения здравного смысла, надо хранить не сумму (алгоритм рассчета которой так же может поменяться), а ставку налога на момент формирования заказа. Впрочем, для финансов, наверное, и сумму тоже имеет смысл хранить.
S>2. Может быть несколько способов расчета суммы, они могут меняться в течении жизненного цикла прогаммы (например завтра будет програссивная шкала налога). S>Как это может повлиять на дизайн?
Повлиять на дизайн может так:
1. Метод рассчета суммы находится внутри класса. При появлении нового алгоритма необходимо модифицировать код класса Order вписав туда еще одну функцию рассчета с реализацией нового алгоритма.
2. Метод рассчета суммы находится вне класса. При появлении нового алгоритма достаточно просто добавить еще одну функцию снаружи, не модифицируя класс Order.
При появлении нового функционала намного предпочтительнее добавлять новый код, а не модифицировать имеющийся.
S>З.Ы. Однако какой знатный холивар пошел
Отож.
Здравствуйте, adontz, Вы писали:
A>Никакое усердно использование правила не заменит усердное использование мозга.
Кто бы спорил. Главное только пользовать его в правильном направлении..
A>Правило — это всего лишь правило и его повсеместное необдуманное применение принесёт больше вреда чем пользы, как и использование любого другого правила.
Вот stump бы на такое обиделся и сказал бы, что он с хамами не разговаривает и что ты вообще любитель передергивать. Но я не гордый, я отвечу.. С чего ты взял, что я за "необдуманное использование правила"? Я где-то призывал не думать? Если так, то тебе наверняка будет не сложно привести нужную цитату.
Проблема ровно в том, что так и не смогли привести ни одного сценария, где бы это правило не работало, и я не вижу ни одной разумной причины его не применять (ну, разьве что по быстрому надо пару классов набросать и забыть).
A> У тебя внешний интерфейс библиотеки — контракт для общения с другими компонентами, построен не на основе понятий предметной области, здравого смысла, и т.п., а на основе исключительно деталей реализации.
Почему исключительно? Вот сейчас ты опять передергиваешь.. Тот самый "здравый смысл", на который вы так все любите ссылаться подсказывает, что при всем желании от деталей реализации никуда не уползешь. Дизайн библиотеки и ее API естественно строится из соображений здравого смысла, понятий предметной области и прочих других красивых слов. Но, к сожалению, иногда, требования производительности идут в разрез с правильным и удобным дизайном и мы вынуждены помещать метод внутрь класса. (Собсственно, Андрей об этом уже писал). Но, при этом, заметь, правило, мной озвученное, по прежнему работает.
A> Всё же специалист твоего уровня должен бы уметь видеть не только отдельные кирпичи но и стену в целом.
Ну вот я и пытаюсь, поднять вас до своего уровня, чтобы вы увидели стену в целом, а вы уперлись тут.. в кирпичик...
Откапываете всякие частности, и пытаетесь придумать сценарий при котором правило не работает — успехов..
A>Понизить инкапсуляцию внутри библиотеки, но предоставить наружу интерфейс не зависящирй от деталей реализации задача более приоритетная.
В том что "зато пользоваться удобно и абстрактно", будешь убеждать пользователя своей библиотеки, когда она начнет считать не правильно.
С высоты моего уровня, приоритеты несколько другие..
A>Если выясниться что метод можно более эффективно реализовать за счёт обращения к внутренним полям класса, то он прыгнет в класс.
Не можно, а нужно. Должны быть очень веские причины, чтобы при возможности использовать только внешний контракт, открывать доступ к внутреннему состоянию.
A> А когда смотришь на всё это снаружи, не углубляясь в детали реализации, то не наблюдаешь абсолютно никакой системы (что не удивительно), сплошной бардак.
Я правильно понимаю, что из за невозможности вынести один метод за пределы класса из соображений производительности, ты предлагаешь все остальные методы так же пихать в класс и получать один большой бардак?
A> Метод LeftSubstring() может быть в строке, а метод RightSubstring() в хелпере. Потому что правило! Твоя цель — логический бардак снаружи, за счёт только тебе, как автору, видной внутренней красоты?
Не угадал. Моя цель — минимизировать бардак, даже в том случае, если мы вынуждены реализовывать несколько методов не там где хотелось бы.
К тому же ты опять передергиваешь, речь не в абстрактной "внутренней красоте", а в удобстве поддержки, даже на твоем уровне это должно быть очевидно..
A>Хелперы имеют один большой недостаток следующий из размазанности функционала по классам — необходимо помнить (sic!) или постоянно искать в документации названия классов для выполнения операций с данным классом.
<...ужасы поскипаны...>
Рома, не придумывай, на практике это не проблема. Для статических хелперов есть методы расширения, для избавления от этой проблемы, а с IoC и так все понятно. Более того, даже без методов расширения это не было большой проблемой.
A> Юзабилити оно бывает не только в интерфейсах. Тебе может шашечки, рейтинги связности считать, другой абстракционистикой маятся, а народу ехать, сейчас и с комфортом. Рейтинги и правила это, конечно, познавательно, но голову никто не отменял.
Гы. То есть, контраргументов на приведенные формальные метрики, прнципы уменьшения связности и прочие best-practice найти не удалось, значит надо обвинить оппонента в тяге к абстрактной красивости..
Ром, я тоже так умею, давай не начинать? Если есть что по делу сказать — излагай, с удовольствием выслушаю.
A> Они по твоей логике вообще не должны существовать,
Давай я в своей логике сам разберусь? Тебя не затруднит привести цитату, где бы я клеймил Extension Methods?
A> ведь они не могут обращаться к внутренним полям класса, а значит должны быть вынесены в хелпер.
А по твоему класс с Extension Method-ами — не абстрактный хелпер? А что же он тогда?
A> Но нет, придумали специальный синтаксис, расширили язык именно для удобного использования методов.
Для удобного использования абстрактных хелперов, Рома. Именно для них..
A>Как видишь, никаких рейтингов связности.
Как это никаких? Как раз специально, для самых ленивых придумали специальную плюшку, позволяющую выносить методы их класса в абстрактные хелперы. Именно для этого, и ни для чего другого. Это именно тот подход, который я отстаиваю...
Ты бы разобрался в вопросе предварительно, прежде чем клеймить..
A> Открой MSDN и прочитай "Implementers of class libraries should not use extension methods".
Андрюша тебя уже вывел на чистую воду, так что не буду тратить время...
В твоей рич модели есть одна проблема — отслеживание изменения суммы ордера. В примере ты отслеживаешь только факты добавления и удаления позиций. Но сами позиции могут измениться (другой товар, другая цена, другое количество) и сумму ордера надо пересчитывать. Тут в рич моделях обычно начинает работать "тяжелая артилерия" вроде патерна обозреватель или АОП. Но в любом случае код сильно усложняется.
Дальше — хуже. Сумма ордера зависит и от продукта (ставка налога). При изменении ставки налога, надо бы пересчитать суммы по ордерам, да не просто так, а по какому нибудь хитрому правилу (например, с определенной даты).
В общем тут пересчет суммы ордера, вынесенный в отдельный сервисный класс, представляется более предпочтительным.
Так и знал, что будут додумывания, появятся вопросы по отсутствующим классам...
S>В твоей рич модели есть одна проблема — отслеживание изменения суммы ордера. В примере ты отслеживаешь только факты добавления и удаления позиций.
Да.
S>Но сами позиции могут измениться (другой товар, другая цена, другое количество) и сумму ордера надо пересчитывать.
Могут, но мы не допустим: сделаем OrderLine immunable.
S>Тут в рич моделях обычно начинает работать "тяжелая артилерия" вроде патерна обозреватель или АОП. Но в любом случае код сильно усложняется.
События тут явно лишние.
Тут лишние. Можно привести примеры задачи когда child-объектам нужно уведомить о чем нибудь своего парента. Но в этом случае лучше использовать цепочку обязанностей, так как инициаторы события (дети) знают о его получателях (родители).
А АОП совсем не причем. Где ты тут разглядел аспект?
S>Сумма ордера зависит и от продукта (ставка налога). При изменении ставки налога, надо бы пересчитать суммы по ордерам, да не просто так, а по какому нибудь хитрому правилу (например, с определенной даты).
Ой, а как это все получилось? Откуда такое бизнесс требование? Неужели ставка налога меняется каждые 3 минуты, час?
Конечно же нет. Каждая ставка утверждается государством. Изменения происходят не чаще раза год. Ну не чаще раза в месяц. Самое главное, что в момент формирования заказа и в момент его оплаты об изменении этой ставки можно забыть!
S>В общем тут пересчет суммы ордера, вынесенный в отдельный сервисный класс, представляется более предпочтительным.
В явно зверской версии ТЗ -- да. Но в реальной ситуации -- нет.
Давно наблюдаю за баталией, мне кажется что баталия сваливается в частности (екстеншн методы, производительность...). Частности, конечно, имеют значение, но хочется все-же вынести какое-то общее правило. Сам я решения про вынос методов принимаю пятой точкой, чему не сильно рад, и собственные попытки сформулировать хоть какой-то принцип ни к чему хорошему не приводят.
В основном обсуждается правило, представленное IB. Применять его тупо ли, с головой ли, со здравым смыслом, с оговорками на производительность ли, как быть с парными методами (LeftSubstring и пр..). Получилась большая каша. Слишком много дополнительных условий, допущений, порой исскувственно введенных, чтобы подначить оппонента
Предлагаю рассмотреть очень простой пример (String — это довольно сложный пример, Order — тоже). Есть в FCL такой класс Rectangle... Он хорош для нашего случая тем, что в нем отсутствуют поля, которые напрямую не доступны через публичный контракт.
Предлагаю искусственно ввести условия для большего упрощения обсуждения:
* Extension методов нет вообще (допустим C# 2.0);
* На производительность нам плевать;
* Так же предлагаю принять что класс Rectangle — reference class (для того, чтобы отпали аргументы, специфичные для value типов);
* Наше пространство евклидово двумерное, стороны прямоугольника параллельны осям (т.е. нет неоднозначностей при определении принадлежности точки либо другого прямоугольника данному прямоугольнику, все реализации методов будут как в FCL);
* Никаких других дополнительных условий не возникнет. Т.е. мы ставим себя на место разработчиков FCL и создаем класс для нужд System.Drawing и System.Windows.Froms (DI нам здесь не понадобится).
Вопрос стандартный для этой темы: надо ли вынести все методы класса Rectangle в другой класс? Почему?
Думаю, что это самый главный кирпич. И если с ним не будет понятно, то дальше будет не интересно.(Надеюсь, что все согласятся с тем, что если откинуть какие-то ограничения, то случай не будет чистым и кто-то будет радеть за правильность, кто-то за производительность, кто-то за reuse, и т.п.)
Что лично я думаю о правиле, предложенном IB: вижу я довольно хороший момент в нем: методы, находящиеся внутри класса нуждаются в дополнительном анализе при принятии решения выноса их из класса при рефакторинге. В то время, очевидно, что метод, находящийся снаружи (в хелпере) может быть подвергнут переносу в другой класс без дополнительного анализа. Под дополнительным анализом подразумеваю анализ возможности использования лишь публичного контракта. Внутренние методы не тяготеют к использованию только публичного контракта.
Итого, с оговоркой на перспективу развития — это правило мне нравится. Но я не склонен выносить методы в хелперы при первой возможности. ))
Здравствуйте, samius, Вы писали: S> ... S>Итого, с оговоркой на перспективу развития — это правило мне нравится. Но я не склонен выносить методы в хелперы при первой возможности. ))
У вам наоборот слишком абстрактный пример и это не хорошо. В том то и дело что на практике все может быть по-другому.
Здравствуйте, samius, Вы писали:
S>Вопрос стандартный для этой темы: надо ли вынести все методы класса Rectangle в другой класс? Почему?
Однозначно оставить.
Стоимость поддержки широкоиспользуемой библиотеки делится на все проекты ее использующие. Да и заявления о повыешнии этой самой стоимости достаточно гиперболизированы.
Я не хочу сам использовать кучу разных хелперов для обработки прямоугольников. Я не хочу разбирать код джуниоров которые вместо одного незнакомого хелпера наделали макарон из знакомых им. Я хочу видеть все методы на одной странице хелпа.
Здравствуйте, AndrewVK, Вы писали:
AVK>По существу я ссылочку привел.
"Надо искать золотую середину, когда и дизайн не слишком плох, и перформанс приемлем."?
S>Есть в FCL такой класс Rectangle... Он хорош для нашего случая тем, что в нем отсутствуют поля, которые напрямую не доступны через публичный контракт.
Фигассе простой пример.
Но: в данном конкретном случае большинство методов, вынесенных в его публичный контракт, Прямоугольнику не нужны. Потому как есть более общие случай -- класс Region. Вот в него и должны быть вынесены эти методы.
Contains: в Region -- new Region(rectangle).IsVisible(point) // Интересное название IsVisible. Почему не Contains?
Inflates ("надувает" прямоугольник): Интересный метод. Не сильно представляю наф он нужен, но если он нужен, то только Прямоугольнику -- остается. // давайте без офтопа зачем он нужен
Intersect (пересечение): в Region
IntersectsWith (проверяет есть ли что-то в пересечении): в Region
Offset (перемещает прямоугольник): наверное, единственный метод который нужен Прямоугольнику -- остается.
Union: в Region
Здравствуйте, samius, Вы писали:
S> Получилась большая каша. Слишком много дополнительных условий, допущений, порой исскувственно введенных, чтобы подначить оппонента
Ёее.. Никакой каши. Принцип прост и понятен, никаких оговорок, исключений, частных сучаев не наблюдается вообще — тем и хорош.
В этом одно из основных преимуществ данного подхода, правило настольно четкое и конкретное, что его можно автоматизировать.
S>Вопрос стандартный для этой темы: надо ли вынести все методы класса Rectangle в другой класс? Почему?
Если изменений класса и методов не предполагается, то вопрос бессмысленный, так как все ухищрения дизайна и архитектуры направлены именно на облегчение поддержки и модификации.
Если же предвидятся какого либо рода модификации (изменение внутренней реализации класса Rectangle, появление новых алгоритмов рассчета площади, периметра, попадания точки внутрь прямоугольника, ect...), то естественно все надо выносить наружу.
1. При добавлении нового функционала не модифицируем существующий прямоугольник, а добавляем новый хелпер/сервис с новым функционалом, что очевидно лучше.
2. Все побочные эффекты изменения реализаций контролирует компилятор.
3. Можно передать наш прямоугольник через слои приложения или вообще запулить наружу через веб-сервис, не боясь неправильного вызова метода или того, что на другой стороне этого метода просто не окажется.
4. Проще тестировать методы, вместо прямоугольника можно спокойно подпихнуть mock.
N. И еще куча других аргументов, которые я уже неоднократно приводил в этом топике, начиная от глубоко теоретических и заканчивая сугубо практическими.
Внятный контраргумент прозвучал только один — неудобно использовать. Но что a.foo(), что b.foo(a) по удобству не отличаются, а цепочки вызовов a.foo().foo().foo(), мягко говоря, по любому не образец удобства.
S>Итого, с оговоркой на перспективу развития — это правило мне нравится.
Без перспективы развития, как я уже говорил, вообще весь разговор бессмысленен. Как показывает практика, в конечном итоге, работает вообще любой код, хоть всю программу в одну строчку, в одном методе напиши. Работать будет и возможно, даже без ошибок, если писать аккуратно.
Весь вопрос — во что обойдется поддержка этого кода.
Здравствуйте, Aikin, Вы писали:
A>ИМХО, вы о разном говорите
Мы говорим ровно об одном и том же..
Если отбросить Ромин искрометный юмор, то все просто как рельс. Мотивируя тем, что по каким-то причинам нам пришлось поместить один метод в класс, Рома призывает запихнуть в этот же класс и все остальные похожие методы, превратив его в большую помойку. Мне же такая мотивация кажется ни разу не убедительной..
Здравствуйте, IB, Вы писали:
IB>Вот stump бы на такое обиделся и сказал бы, что он с хамами не разговаривает и что ты вообще любитель передергивать.
Нет, он он мне уже сказал
Большой +
Все эти аргументы и многие другие уже приводились здесь. Ты действительно хорошо пишешь, но сомневаюсь, что тебе удастся в чем то убедить Ивана и Андрея.
Видимо передёргиваешься всё же ты, пытаясь прикрыться людьми, которые вовсе не хотят, чтобы ты ими прикрывался. Неужели так мало осталось соратников? Неужто раскрыта ложность твоих идеалов и все разбежались?
IB>Проблема ровно в том, что так и не смогли привести ни одного сценария, где бы это правило не работало, и я не вижу ни одной разумной причины его не применять (ну, разьве что по быстрому надо пару классов набросать и забыть).
А тебе никто не приведёт пример, когда правило не получается применить. Его получается применить всегда, однако, время от времени, это приводит к абсурдному результату, когда логически связанная группа методов оказывается разбросана по разным классам.
IB>Почему исключительно?
Потому что правило. Ты ведь принимаешь его как догму, без оговорок.
IB>Тот самый "здравый смысл", на который вы так все любите ссылаться подсказывает, что при всем желании от деталей реализации никуда не уползешь.
Нельзя уползти от предметной области, от деталей реализации можно и нужно уползать.
IB>Но, к сожалению, иногда, требования производительности идут в разрез с правильным и удобным дизайном и мы вынуждены помещать метод внутрь класса. (Собсственно, Андрей об этом уже писал). Но, при этом, заметь, правило, мной озвученное, по прежнему работает.
Правильный и красивый дизайн библиотеки подразумевает в первую очередь удобство использования, а не лёгкость поддержки. Не надо быть лентяем за счёт других.
IB>Ну вот я и пытаюсь, поднять вас до своего уровня, чтобы вы увидели стену в целом, а вы уперлись тут.. в кирпичик...
Поднять?! Переверни шкалу отсчёта, ты её неправильно держишь
IB>Откапываете всякие частности, и пытаетесь придумать сценарий при котором правило не работает — успехов..
Во-первых, не пытаюсь, а уже не раз привёл, чиатй внимательнее, разжевал я свой пример и в рот положил. Конкретного ответа так и не получил, оно и не удивительно, ведь пришлось бы признать что правило не работает. Во-вторых, сферические кони в вакууме интересуют только абстракционистов-теоретиков, а у реальных людей всегда есть частные варианты с которыми тоже надо что делать. Причём на совесть, а не прикрываться правилами.
IB>В том что "зато пользоваться удобно и абстрактно", будешь убеждать пользователя своей библиотеки, когда она начнет считать не правильно.
Что за фигня? С чего это она начнёт считать неправильно? Что за нелепый, абсурдный аргумент? Тебе нечего было больше ответить, кроме как "если не использовать моё правило, у вас будут баги"?
IB>Не можно, а нужно. Должны быть очень веские причины, чтобы при возможности использовать только внешний контракт, открывать доступ к внутреннему состоянию.
Например, здравый смысл
IB>Я правильно понимаю, что из за невозможности вынести один метод за пределы класса из соображений производительности, ты предлагаешь все остальные методы так же пихать в класс и получать один большой бардак?
Я чуть ниже отвечу на этот важный вопрос.
IB>Не угадал. Моя цель — минимизировать бардак, даже в том случае, если мы вынуждены реализовывать несколько методов не там где хотелось бы.
Не там где хотелось бы кому? Тебе или пользователю библиотеки? Мне как пользователю хелпер не удобен по определению.
IB>К тому же ты опять передергиваешь, речь не в абстрактной "внутренней красоте", а в удобстве поддержки, даже на твоем уровне это должно быть очевидно..
Удобство поддержки при написании библиотеки не является более приоритетным, чем качественный внешний интерфейс. На кой ляд хорошо поддерживаемая бюиблиотека, если ей неудобно пользоваться?
IB>Рома, не придумывай, на практике это не проблема. Для статических хелперов есть методы расширения, для избавления от этой проблемы, а с IoC и так все понятно. Более того, даже без методов расширения это не было большой проблемой.
Методы расширения для другой цели, я напишу об этом ниже.
IB>Гы. То есть, контраргументов на приведенные формальные метрики, прнципы уменьшения связности и прочие best-practice найти не удалось, значит надо обвинить оппонента в тяге к абстрактной красивости..
Ты действительно думаешь что твой или ещё какой-то рейтинг чем-то принципиально осмысленнее чем LOC/day и его область применения не узка?
A>> ведь они не могут обращаться к внутренним полям класса, а значит должны быть вынесены в хелпер. IB>А по твоему класс с Extension Method-ами — не абстрактный хелпер? А что же он тогда?
Средство расширения существующей иерархии классов (построения параллельной, горизонтальной, жалкое подобие duck typing) с сохранением удобства использования.
IB>Как это никаких? Как раз специально, для самых ленивых придумали специальную плюшку, позволяющую выносить методы их класса в абстрактные хелперы. Именно для этого, и ни для чего другого. Это именно тот подход, который я отстаиваю... IB>Ты бы разобрался в вопросе предварительно, прежде чем клеймить..
А вот и то самое "ниже", которое я дважды обещал. Вот пример, наглядный. Есть методы A и B у класса C. A требует доступа к приватной перменной _x, B не требует.
class С
{
private int _x;
public void A()
{
// use _x
}
public void B()
{
}
}
Это плохо. Что ты предлагаешь сделать? Ты предлагаешь сделать так
class С
{
private int _x;
public void A()
{
// use _x
}
}
class СHelper
{
public static void B(C c)
{
}
}
или даже так
class С
{
private int _x;
public void A()
{
// use _x
}
}
class СHelper
{
public static void B(this C c)
{
}
}
при этом самое очевидное решение, ты почему то в упор не видишь
class СBase
{
private int _x;
public void A()
{
// use _x
}
}
class С : CBase
{
public void B()
{
}
}
Это решение было всегда, и в C#, и в Си++ и в Object Pascal.
Оставляю тебе на домашнее задание разобраться зачем на самом деле были введены extension methods. Ну или просто прочти это сообщение внимательно, я уже написал
Здравствуйте, Ziaw, Вы писали:
Z>Однозначно оставить.
Z>Стоимость поддержки широкоиспользуемой библиотеки делится на все проекты ее использующие.
А стоимость ошибки в реализации?
Z>Да и заявления о повыешнии этой самой стоимости достаточно гиперболизированы.
А заявления об ужасах поиска нужного метода — нет?
Z> Я не хочу разбирать код джуниоров которые вместо одного незнакомого хелпера наделали макарон из знакомых им.
Ищите внятных джуниоров.
Z>Я хочу видеть все методы на одной странице хелпа.
Правильно. На одной странице хелпа к нужному сервису.
Здравствуйте, adontz, Вы писали:
A>Более конкретного ответа я и не ожидал
А более конкретного ответа в отсутствие реальных требований предоставить и не возможно. Мне это напоминает собеседование в одной очень известной конторе — когда предлагали спроектировать какое нибудь простенькое приложение, не уточняя масштабов проекта, требований к расширяемости, предполагаемой длительности жизненного цикла etc.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, samius, Вы писали:
S>Что лично я думаю о правиле, предложенном IB: вижу я довольно хороший момент в нем: методы, находящиеся внутри класса нуждаются в дополнительном анализе при принятии решения выноса их из класса при рефакторинге. В то время, очевидно, что метод, находящийся снаружи (в хелпере) может быть подвергнут переносу в другой класс без дополнительного анализа. Под дополнительным анализом подразумеваю анализ возможности использования лишь публичного контракта. Внутренние методы не тяготеют к использованию только публичного контракта. S>Итого, с оговоркой на перспективу развития — это правило мне нравится. Но я не склонен выносить методы в хелперы при первой возможности. ))
Это частный случай вашего правила доведенный до абсурда. Рома же говорил не только об этом.
IB>Мотивируя тем, что по каким-то причинам нам пришлось поместить один метод в класс, Рома призывает запихнуть в этот же класс и все остальные похожие методы, превратив его в большую помойку.
Вы же считаете, что похожие методы должны быть в разных местах. Ага. А потом будут соревнования по скростному поиску методов так как:
Метод LeftSubstring() может быть в строке, а метод RightSubstring() в хелпере
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, samius, Вы писали:
IB>1. При добавлении нового функционала не модифицируем существующий прямоугольник, а добавляем новый хелпер/сервис с новым функционалом, что очевидно лучше.
Судя по флейму не так уж очевидно.
IB>2. Все побочные эффекты изменения реализаций контролирует компилятор.
Может стоит ввести в язык новый модификатор метода который имеет доступ только к публичным членам?
IB>3. Можно передать наш прямоугольник через слои приложения или вообще запулить наружу через веб-сервис, не боясь IB> неправильного вызова метода или того, что на другой стороне этого метода просто не окажется.
Со всеми инжектнутыми сервисами внутри.
IB>4. Проще тестировать методы, вместо прямоугольника можно спокойно подпихнуть mock.
Что мешает мокать при вызове собственных методов?
Здравствуйте, adontz, Вы писали:
A>Видимо передёргиваешься всё же ты, пытаясь прикрыться людьми, которые вовсе не хотят, чтобы ты ими прикрывался. Неужели так мало осталось соратников? Неужто раскрыта ложность твоих идеалов и все разбежались?
Понятно, аргументы по делу кончились, я так и думал..
Не на долго же тебя в этот раз хватило..
A>А тебе никто не приведёт пример, когда правило не получается применить.
Конечно, его же нет..
A>Нельзя уползти от предметной области, от деталей реализации можно и нужно уползать.
Засчет сваливания всего в одну кучу? Отличный способ.
A>Правильный и красивый дизайн библиотеки подразумевает в первую очередь удобство использования, а не лёгкость поддержки.
Весьма спорный тезис. Если удобству использования приносится в жертву надежность, то нафиг-нафиг такую библиотеку.
A>Поднять?! Переверни шкалу отсчёта, ты её неправильно держишь
Родной — это ты шкалу ввел, забыл уже? =) Как-то ты быстро свою позицию меняешь..
A> Во-вторых, сферические кони в вакууме интересуют только абстракционистов-теоретиков, а у реальных людей всегда есть частные варианты с которыми тоже надо что делать. Причём на совесть, а не прикрываться правилами.
Да и "реальные люди", навалили в класс кучу методов не разбираясь, зато никаких правил исключительно здравым смыслом руководствовались! =)
Отличный результат.. )
A> С чего это она начнёт считать неправильно?
С того что код получился менее надежный и в нем легче ошибиться при модификации.
A>Что за нелепый, абсурдный аргумент?
Если он тебе не нравится — это не значит что он не лепый и абсурдный..
A>Мне как пользователю хелпер не удобен по определению.
Придется переучиваться..
A>Удобство поддержки при написании библиотеки не является более приоритетным, чем качественный внешний интерфейс. На кой ляд хорошо поддерживаемая бюиблиотека, если ей неудобно пользоваться?
На такой, что она более надежна.
A>Методы расширения для другой цели, я напишу об этом ниже.
=)
A>Ты действительно думаешь что твой или ещё какой-то рейтинг чем-то принципиально осмысленнее чем LOC/day и его область применения не узка?
Ты что этим сказать-то хотел?
A>Средство расширения существующей иерархии классов (построения параллельной, горизонтальной, жалкое подобие duck typing) с сохранением удобства использования.
Да, и для этого хелперы тоже можно использовать..
A>А вот и то самое "ниже", которое я дважды обещал. Вот пример, наглядный. Есть методы A и B у класса C. A требует доступа к приватной перменной _x, B не требует.
<...> A>Это решение было всегда, и в C#, и в Си++ и в Object Pascal.
Потому что наследование катастрофически увеличивает связность. При нормальном дизайне случаи использования наседования можно по пальцам пересчитать. Агрегация в большинстве случаев намного предпочтительнее.
A>Оставляю тебе на домашнее задание разобраться зачем на самом деле были введены extension methods.
Сначала разберись, чем плохо наследование..
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Ziaw, Вы писали:
Z>>Однозначно оставить. IB>
Z>>Стоимость поддержки широкоиспользуемой библиотеки делится на все проекты ее использующие. IB>А стоимость ошибки в реализации?
Умножается. Только не верю я в вынос методов как в эффективную меру борьбы с ошибками реализации.
Тестирования, ревью кода, скил программистов решают эту проблему гораздо эффективнее.
Z>>Да и заявления о повыешнии этой самой стоимости достаточно гиперболизированы. IB>А заявления об ужасах поиска нужного метода — нет? Z>> Я не хочу разбирать код джуниоров которые вместо одного незнакомого хелпера наделали макарон из знакомых им. IB>Ищите внятных джуниоров.
Ужасов не будет если специалист узконаправлен и наизусть знает данную библиотеку. Если вдруг понадобилось быстро разобраться — ничуть не гиперболизированы. Порог вхождения не менее важный фактор для библиотеки чем стоимость ее поддержки.
Z>>Я хочу видеть все методы на одной странице хелпа. IB>Правильно. На одной странице хелпа к нужному сервису.
Насколько я понял мысль — сервисов планируется не один.
Здравствуйте, minorlogic, Вы писали:
M>Если ссылка еще не пробегала.
Перевод там, конечно ...
Цитаты по теме:
Мы теперь видим, что приемлемый способом оценки инкапсуляции является количество функций, которые могли бы быть разрушены, если изменяется реализация класса. В этом случае становится ясно, что класс с n методами более инкапсулирован, чем класс с n+1 методами. И это наблюдение поясняет мое предпочтение в выборе функций, не являющихся ни друзьями, ни методами: если функция f может быть выполнена как метод или как функция, не являющаяся другом, то создание ее в виде метода уменьшило бы инкапсуляцию, тогда как создание ее в виде "недруга" инкапсуляцию не уменьшит. Так как функциональность здесь не обсуждается (функциональные возможности f доступны классам клиентов независимо от того, где эта f размещена), мы естественно предпочитаем более инкапсулированный проект.
...
Если Вы самокритичны и честны сами с собой, вы увидите, что имеете эту предполагаемую несогласованность со всеми нетривиальными классами, Вы используете ее потому, что класс не может имеет любую функцию, которую пожелает како-то клиент. Каждый клиент добавляет, по крайней мере, несколько своих функций для собственного удобства, и эти функции всегда не являются методами классов. Программисты на C++ используют это, и они не думают ничего о этом. Некоторые вызовы используют синтаксис методов, а некоторые синтаксис внешних вызовов. Клиенты только ищут, какой из синтаксисов является соответствующим для тех функций, которые они хотят вызвать, затем они вызывают их. Жизнь продолжается. (это по поводу Роминых StringLeft и StringRight)
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, adontz, Вы писали:
A>Не надо вестись на провокации Бодягина, есть третий путь
К слову, самый худший из всех предложенных..
"Нафига нам враги когда у нас есть такие друзья" (с) =)))
Здравствуйте, Ziaw, Вы писали:
Z>Судя по флейму не так уж очевидно.
???
Я этот аргумент выдвигал раз пять. Каждый раз его молча игнорировали, отсюда делаем вывод, что возразить нечего.
Z>Может стоит ввести в язык новый модификатор метода который имеет доступ только к публичным членам?
Возможно, но я бы предпочел методы расширения в экземплярных классах...
Z>Со всеми инжектнутыми сервисами внутри.
Зачем? Наоборот это голый объект, который можно передать в любой сервис умеющий с ним работать.
Z>Что мешает мокать при вызове собственных методов?
Методы придется тестировать вместе с классом, а так можно по отдельности.
Я против введения базового класса без необходимости в полиморфизме а только лишь для группировки методов и контроля компилятором соблюдения публичного контракта. Это может привести потребителя к лишней неоднозначности. Для этой цели лучше уж хелпер.
Здравствуйте, Ziaw, Вы писали:
Z> Только не верю я в вынос методов как в эффективную меру борьбы с ошибками реализации.
Я тоже не верю.. Я на практике проверял, более чем эффективна.
Z>Тестирования, ревью кода, скил программистов решают эту проблему гораздо эффективнее.
А уж все в месте.. )
Z>Ужасов не будет если специалист узконаправлен и наизусть знает данную библиотеку.
Не надо знать наизусть. Достаточно знать что сущность имеет хелпер. Если же вся библиотека спроектирована в таком стиле, то очевидно, что сущность имеет хелпер.
(Под хелпером следует понимать хелпер, сервис, любой внешний функционал по ее обслуживанию) Внутрення логика хорошо прописанной библиотеки постигается в течении нескольких дней, дальше никаких проблем.
Z>Насколько я понял мысль — сервисов планируется не один.
Какая разница, если они четко сгруппированы по функциональному признаку и в хелпе они в любом случае проассоциированы с объектом, с которым работают?
Здравствуйте, Aikin, Вы писали:
A>Так и знал, что будут додумывания, появятся вопросы по отсутствующим классам...
Да нет же, мне вполне понятно общее направление твоей мысли. Я просто хочу уточнить критерии, которыми ты наверняка руководствовался, но не упомянул о них.
A>События тут явно лишние. A>Тут лишние. Можно привести примеры задачи когда child-объектам нужно уведомить о чем нибудь своего парента. Но в этом случае лучше использовать цепочку обязанностей, так как инициаторы события (дети) знают о его получателях (родители).
Пока не знают, но сделаем чтоб знали, согласен. Вот тут возникает вопрос: тем самым мы еще больше усиливаем связность между Order и OrderLine, что вроде бы плохо в общем случае. Но в этом конкретном случае на усиление связи можно мело наплевать, нам понятно что мы никогда не будем использовать OrderLine отдельно от Order.
Это к вопросу о необходимости проектирования по "четко определенным" критериям.
A>А АОП совсем не причем. Где ты тут разглядел аспект?
При изменении любого свойства надо кого-то оповещать. Как-то так...
A>Конечно же нет. Каждая ставка утверждается государством. Изменения происходят не чаще раза год. Ну не чаще раза в месяц. Самое главное, что в момент формирования заказа и в момент его оплаты об изменении этой ставки можно забыть!
Ну ставка налога да. Но на ее месте может быть тарифная ставка, или вообще какая нибудь скидка (суперакция! только один день 31 июня! скидка 50%! спешите персчитать все ваши отложенные ордера! ) S>>В общем тут пересчет суммы ордера, вынесенный в отдельный сервисный класс, представляется более предпочтительным. A>В явно зверской версии ТЗ -- да. Но в реальной ситуации -- нет.
Бизнес требования порой превосходят любую нашу фантазию...
Здравствуйте, IB, Вы писали:
IB>Я этот аргумент выдвигал раз пять. Каждый раз его молча игнорировали, отсюда делаем вывод, что возразить нечего.
Про лучше/хуже говорят остальные пункты.
Z>>Может стоит ввести в язык новый модификатор метода который имеет доступ только к публичным членам? IB>Возможно, но я бы предпочел методы расширения в экземплярных классах...
Я бы тоже не отказался. Насчет предпочел не понял, вроде не было ограничения либо/либо.
Z>>Что мешает мокать при вызове собственных методов? IB>Методы придется тестировать вместе с классом, а так можно по отдельности.
Вместе с моком? Какая разница как передавать мок? Как this или параметром?
Разница только в доступе метода к закрытым членам, но это обсуждается выше.
Здравствуйте, IB, Вы писали:
A>>А тебе никто не приведёт пример, когда правило не получается применить. IB>Конечно, его же нет..
Видимо ты совсем не понимаешь, что возможности применения правила и оправданность применения правила суть разные вещи.
A>>Правильный и красивый дизайн библиотеки подразумевает в первую очередь удобство использования, а не лёгкость поддержки. IB>Весьма спорный тезис. Если удобству использования приносится в жертву надежность, то нафиг-нафиг такую библиотеку.
Это уже ты придумал, что что-то приноситься в жертву. А тезис не спортный, библиотека одна, приложений много. Об этом уже говорили, но ты, видимо, пропустил.
A>> С чего это она начнёт считать неправильно? IB>С того что код получился менее надежный
Фантазии или догматический страх? Видимо тебе приходиться придумывать аргументы, очень жаль.
IB>Придется переучиваться..
Ради святого правила? Не, не айс.
IB>На такой, что она более надежна.
Это не правда, это домыслы. Видимо тебе приходиться придумывать аргументы, очень жаль.
A>>Ты действительно думаешь что твой или ещё какой-то рейтинг чем-то принципиально осмысленнее чем LOC/day и его область применения не узка? IB>Ты что этим сказать-то хотел?
Я хотел сказать что твой или ещё какой-то рейтинг принципиально ничуть не осмысленнее чем LOC/day и его область применения узка.
IB>Да, и для этого хелперы тоже можно использовать..
Много чего можно делать через задницу.
A>>Это решение было всегда, и в C#, и в Си++ и в Object Pascal. IB>Потому что наследование катастрофически увеличивает связность. При нормальном дизайне случаи использования наседования можно по пальцам пересчитать. Агрегация в большинстве случаев намного предпочтительнее.
Глупости, связность никак не увеличилась, связь между хелпером и классом была и так и она не стала сильнее. В данном конктерном случае есть организационное преимущество. Мы делаем расширитель конкретного класса. Хелперы не связаны с расширяемыми классами синтаксически связями и, как результат, хелперы часто превращаются в свалку методов не влезающих в другие классы. Свалку методов разных классов, хорошо ещё если классов одного семейства. Наследование позволяет строго ограничить что именно данный расширитель расширяет. Это и поддерживаемость кода улучшает и ошибки уменьшает
S>Пока не знают, но сделаем чтоб знали, согласен. Вот тут возникает вопрос: тем самым мы еще больше усиливаем связность между Order и OrderLine, что вроде бы плохо в общем случае. Но в этом конкретном случае на усиление связи можно мело наплевать, нам понятно что мы никогда не будем использовать OrderLine отдельно от Order.
+1 Но я все же за иммунабл.
A>>А АОП совсем не причем. Где ты тут разглядел аспект? S>При изменении любого свойства надо кого-то оповещать. Как-то так...
Аспектно-ориентированное программирование (АОП) — парадигма программирования, основанная на идее разделения функциональности, особенно сквозной функциональности, для улучшения разбиения программы на модули.
ИМХО, аспект -- что-то общее для приложения, а не для конкретного класса или связи классов, что-то что можно добавить к уже написанному, отлаженному и хорошо работающему классу.
Аспектом может быть логирование (наиболее частоприводимый пример), секьюрити (очень удобно делать секьюрити на аспектах, так как оно не должно входить в обязанности защищаемых объектов).
S>Ну ставка налога да. Но на ее месте может быть тарифная ставка, или вообще какая нибудь скидка (суперакция! только один день 31 июня! скидка 50%! спешите персчитать все ваши отложенные ордера! )
Да, косячек какой-то. Вводить метод RecalculateTotal() не хочется. Надо обдумать этот момент. Как придумаю (если придумаю) отпишусь.
S>>>В общем тут пересчет суммы ордера, вынесенный в отдельный сервисный класс, представляется более предпочтительным. A>>В явно зверской версии ТЗ -- да. Но в реальной ситуации -- нет. S>Бизнес требования порой превосходят любую нашу фантазию...
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Ziaw, Вы писали:
Z>> Только не верю я в вынос методов как в эффективную меру борьбы с ошибками реализации. IB>Я тоже не верю.. Я на практике проверял, более чем эффективна.
Т.е. был поставлен эксперимент? Можно узнать подробности?
Точные данные по трудозатратам на редизайн.
Точные данные сэкономленого времени на порог вхождения.
Без таких данных я предпочитаю говорить верю, хотя мне и кажется, что знаю.
Я на практике тоже покушал хелперов и порефакторил.
IB>Не надо знать наизусть. Достаточно знать что сущность имеет хелпер. Если же вся библиотека спроектирована в таком стиле, то очевидно, что сущность имеет хелпер. IB>(Под хелпером следует понимать хелпер, сервис, любой внешний функционал по ее обслуживанию) Внутрення логика хорошо прописанной библиотеки постигается в течении нескольких дней, дальше никаких проблем. IB>Какая разница, если они четко сгруппированы по функциональному признаку и в хелпе они в любом случае проассоциированы с объектом, с которым работают?
Давай так, каждый выберет пример библиотеки реализованой по удобной ему идеологии и попытаемся их сравнить.
Я готов поверить, что теоретически можно спроектировать так сказочно, просто все эти библиотеки гдето за NDA наверное.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, samius, Вы писали:
S>>Это может привести потребителя к лишней неоднозначности.
A>Как ми образов если он не видит базовый класс?
Видит производный но не видит базовый? так не бывает. У базового видимость не меньше чем у производного.
Здравствуйте, adontz, Вы писали:
A>А вот и то самое "ниже", которое я дважды обещал. Вот пример, наглядный. Есть методы A и B у класса C. A требует доступа к приватной перменной _x, B не требует.
Вот с этим я сильно не согласен. С увеличением количества приватных переменных _x получаем некислый комбинаторный взрыв наследования.
Здравствуйте, minorlogic, Вы писали:
M>http://www.softcraft.ru/coding/sm/sm01.shtml
M>Если ссылка еще не пробегала.
Так вот откуда ноги растут у "железного правила"
Дело в том, что во-первых правило Мейерса сформулировано не столь категорично как "железное правило Бодягина". Мейерс вспомнил про виртуализацию и про особые случаи.
Во-вторых, Мейерс сформулировал свое правило для C++, с его особенностями реализации OOP, и основные бенефиты, которые он там описывает не работают в C# (хотя само правило, за исключением специфической C++ятины, работает конечно).
В общем, как тут уже говорили "использование правил не отменяет использование головы".
Здравствуйте, Ziaw, Вы писали:
Z>Вот с этим я сильно не согласен. С увеличением количества приватных переменных _x получаем некислый комбинаторный взрыв наследования.
Почему у тебя количество наследников отличается от количества хелперов? В чём усложнение?
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, samius, Вы писали:
S>>Видит производный но не видит базовый? так не бывает. У базового видимость не меньше чем у производного.
A>Не может содавать базовый, базовый не фигурирует нигде. И в чём тогда разница?
Я так понимаю, что в случае C : CBase класс C принимает роль хелпера. Значит для метода B1() может понадобиться класс C1 : CBase, и т.д.
Но тогда не удастся вызвать методы B() и B1() для одного экземпляра объекта. А в случае хелперов без наследования это возможно в виде BHelper.B(c) и B1Helper.B1(c). Либо методы B и B1 в классе C, либо в хелперах.
Здравствуйте, samius, Вы писали:
S>Но тогда не удастся вызвать методы B() и B1() для одного экземпляра объекта. А в случае хелперов без наследования это возможно в виде BHelper.B(c) и B1Helper.B1(c). Либо методы B и B1 в классе C, либо в хелперах.
Да, наследник один. Это минус, вне всякого сомнения, но я на практике не сталкивался с логической необходимостью делать больше одного хелпера. Я предпочитаю partial классы. Формально класс один, но и разбиение методов на группы есть. Все счастливы
Здравствуйте, adontz, Вы писали:
A>Почему у тебя количество наследников отличается от количества хелперов? В чём усложнение?
Прошу прощения
Я поторопился и развил мысль дальше =) Когда кто-то добавит приватное поле в наследника.
Если этого не делать никакого отличия нет. Кроме того, что хелперов можно сделать несколько без ущерба для функционала, а наследеников нельзя.
Здравствуйте, Ziaw, Вы писали:
IB>>Я тоже не верю.. Я на практике проверял, более чем эффективна. Z>Т.е. был поставлен эксперимент? Можно узнать подробности? Z>Точные данные по трудозатратам на редизайн. Z>Точные данные сэкономленого времени на порог вхождения.
Да ничего он тебе не ответит. Одни слова. Как задаешь конкретный вопрос — получаешь ответ не по теме.
Z>Давай так, каждый выберет пример библиотеки реализованой по удобной ему идеологии и попытаемся их сравнить.
Ему уже был задан вопрос привести пример когда на практике есть (по его словам) неудобства с использованием класса String. Ответа не было.
Для C++ этот алгоритм отлично работает. А C# все осложняется тем, что свободных функций нет (поправьте меня, если я ошибаюсь). Использование классов-помощников не всегда удобно. Вот отсюда и конфликт.
Здравствуйте, Ziaw, Вы писали:
Z>Т.е. был поставлен эксперимент? Можно узнать подробности?
Просто пишется продакшн код.
Z>Точные данные по трудозатратам на редизайн.
Зачем редизайн? Так пишется с самого начала.
Z>Точные данные сэкономленого времени на порог вхождения.
Репорты из QA отдела.
Вообще в нашей кампании проблема порога вхождения решается очень просто. Любой новый человек на проекте должен сдать экзамен, в том числе и на знание библиотк. Это правило введено очень давно, еще матерыми C++-ками, которые писали как раз в любимом вами стиле, с кучей методов в классе засунутых туда с "использованием здравого смысла".
Проблему нахождения нужного метода и знания того, что в принципе библиотека умеет, а что нет — решается на корню, при любом раскладе.
Z>Давай так, каждый выберет пример библиотеки реализованой по удобной ему идеологии и попытаемся их сравнить.
Да практически каждая вторая Явовская библиотека так пишется. Из .Net-овских, например, WCF — там порог вхожждения высокий, но это с предметной областью связано, которая сама по себе не простая.
Здравствуйте, stump, Вы писали:
S>Так вот откуда ноги растут у "железного правила"
Ноги у него растут так же из Саттера, Александресу и Мак-конела, практики, и, прости господи, "здравого смысла".
S>Дело в том, что во-первых правило Мейерса сформулировано не столь категорично как "железное правило Бодягина".
Где ты категоричность нашел?
S>В общем, как тут уже говорили "использование правил не отменяет использование головы".
А кто говорил, что отменяет? Можно цитату?
Итого:
Само правило вам не нравится, но логичных аргументов, почему оно может не работать, так придумать и не получилось. Осталось только обвинить меня в догматизме и слепом следовании правилу. =)
Можно констатировать ярчайший пример демагогии со стороны противников "правила" и закрыть тему.
Здравствуйте, Ziaw, Вы писали:
Z>Про лучше/хуже говорят остальные пункты.
Причем тут остальные пункты? Это очевидный плюс, никак не зависящий от остальных пунктов.
Z>Насчет предпочел не понял, вроде не было ограничения либо/либо.
Если бы они были, то специальная конструкция была бы лишней.
Z>Вместе с моком? Какая разница как передавать мок? Как this или параметром?
Речь не пр this, а про то, куда девать функцию содержащую сам алгоритм реализации.
Здравствуйте, adontz, Вы писали:
A>Как можно аргументированно опровергать чьи-то галлюцинации?
Ром, каждый раз, когда общаюсь с тобой — сталкиваюсь с этой проблемой..
Обычно удается, но озлобляет страшно — приходится опускаться до твоего уровня. =)
Здравствуйте, adontz, Вы писали:
A>Видимо ты совсем не понимаешь, что возможности применения правила и оправданность применения правила суть разные вещи.
Ты нашел один пример, в которм, по твоему мнению, применение правила не оправдано. Однако, очевидно, его не применение приводит к еще худшим последствиям. Значит применение по прежнему считаем оправданым.
Еще примеры неоправданности будут?
A>Это уже ты придумал, что что-то приноситься в жертву.
Я не придумал, я подробно, и не раз, расписал почему так происходт, тебе лень прочитать?
A> А тезис не спортный, библиотека одна, приложений много.
И какова при таком раскладе цена ошибки в библиотеке?
A>Об этом уже говорили, но ты, видимо, пропустил.
Нет, Ром, это ты пропустил, что я целиком согласен с этим тезисом, так как он подтверждает мою позицию..
A>Я хотел сказать что твой или ещё какой-то рейтинг принципиально ничуть не осмысленнее чем LOC/day и его область применения узка.
Ты уж определись, LOC — бессмысленен или узок? =)
A>Глупости, связность никак не увеличилась,
Учи домашнее задание, тема — наследование..
Здравствуйте, adontz, Вы писали:
A> Это проблема и я её решаю за счёт partial классов. Нет, конечно я не отказался от хелперов совсем, просто есть и другие пути.
Еще более кривые..
Здравствуйте, IB, Вы писали:
IB>Итого: IB>Само правило вам не нравится, но логичных аргументов, почему оно может не работать, так придумать и не получилось. Осталось только обвинить меня в догматизме и слепом следовании правилу. =) IB>Можно констатировать ярчайший пример демагогии со стороны противников "правила" и закрыть тему.
Опять по существу ничего ответить не нашлось.
Ну тогда тебе только и остается констатировать — все, что несоответствует правилу Бодягина есть демагогия.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Ziaw, Вы писали:
Z>>Т.е. был поставлен эксперимент? Можно узнать подробности? IB>Просто пишется продакшн код. IB>Репорты из QA отдела.
Раз все идет хорошо, значит наш путь единственно верный? Таким образом доказывается только то, что этот путь не убивает проект. Как и многие другие.
Z>>Точные данные по трудозатратам на редизайн. IB>Зачем редизайн? Так пишется с самого начала.
хорошо, примем за 0.
IB>Вообще в нашей кампании проблема порога вхождения решается очень просто. Любой новый человек на проекте должен сдать экзамен, в том числе и на знание библиотк. Это правило введено очень давно, еще матерыми C++-ками, которые писали как раз в любимом вами стиле, с кучей методов в классе засунутых туда с "использованием здравого смысла". IB>Проблему нахождения нужного метода и знания того, что в принципе библиотека умеет, а что нет — решается на корню, при любом раскладе.
Т.е. проблема порога решается просто обрубанием той части людей которые о него споткнулись. Применимо только в случае когда людям хорошо платят за использование вашего фреймворка.
Z>>Давай так, каждый выберет пример библиотеки реализованой по удобной ему идеологии и попытаемся их сравнить. IB>Да практически каждая вторая Явовская библиотека так пишется. Из .Net-овских, например, WCF — там порог вхожждения высокий, но это с предметной областью связано, которая сама по себе не простая.
Походу мне попадались каждые первые, впрочем я не знаток явовских библиотек, можно ткнуть пальцем?
WCF тут непонятно каким боком.
Где там преобладание статик хелперов?
Все что я помню из статиков в WCF является либо синглтоном либо фабрикой.
Uri baseAddress = new Uri("http://localhost:8000/HelloService");
string address = "http://localhost:8000/HelloService/MyService";
using (ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddress))
{
serviceHost.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(), address);
serviceHost.Open();
Console.WriteLine("Press <enter> to terminate service");
Console.ReadLine();
serviceHost.Close();
}
Как результат — порог вхождения у него ниже некуда. Можно не сдавая экзаменов по WCF (да и вообще первый раз увидев эту библиотеку) через 15 минут получить работающее сетевое приложение.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Ziaw, Вы писали:
Z>>Про лучше/хуже говорят остальные пункты. IB>Причем тут остальные пункты? Это очевидный плюс, никак не зависящий от остальных пунктов.
Толковый словарь русского языка Ушакова
ОЧЕВИ'ДНЫЙ, ая, ое; -ден, дна, дно.
1. Несомненный, бесспорный, такой явный, что можно убедиться собственными глазами. Очевидная истина. Это — очевидное недоразумение. О. промах. В ней произошла перемена — это было очевидно. Тргнв. Очевидна цель его объяснений — успокоить меня. Чрншвскй. [Первонач. заметный, видимый взору. Сундуки его полнели очевидно. Крлв.] 2. Видевший своими глазами, являющийся очевидцем (старин.). Очевидные свидетели.
Судя по ветке, не плюс явно не бесспорный.
Z>>Насчет предпочел не понял, вроде не было ограничения либо/либо. IB>Если бы они были, то специальная конструкция была бы лишней.
Если бы они были, они бы имели доступ к приватным членам и ничем не отличались бы от обычного метода.
Z>>Вместе с моком? Какая разница как передавать мок? Как this или параметром? IB>Речь не пр this, а про то, куда девать функцию содержащую сам алгоритм реализации.
Нить куда-то пропала, я пожалуй восстановлю:
IB>4. Проще тестировать методы, вместо прямоугольника можно спокойно подпихнуть mock. Z>Что мешает мокать при вызове собственных методов? IB>Методы придется тестировать вместе с классом, а так можно по отдельности. Z>Вместе с моком? Какая разница как передавать мок? Как this или параметром? IB>Речь не пр this, а про то, куда девать функцию содержащую сам алгоритм реализации.
Если имеется ввиду расположение тестов — их можно располагать где угодно, если имеется нечто другое — не пойму что.
Здравствуйте, IB, Вы писали:
A>> Это проблема и я её решаю за счёт partial классов. Нет, конечно я не отказался от хелперов совсем, просто есть и другие пути. IB>Еще более кривые..
Сомнительно, я ведь использую все возможности языка, а ты помешался на конкретных.
Здравствуйте, Ziaw, Вы писали:
Z>Где там преобладание статик хелперов?
Там не статик хелперы, там DI.
Z>Как результат — порог вхождения у него ниже некуда.
Это не тот порог. Чтобы написать простенький код — да, скопировал пример и вперед. А вот если нужно понимать, что происходит (а это обязательное условие, если ты это применяешь в серьезном проекте) — вот тут все становится сразу печально.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
IB>Однако, очевидно, его не применение приводит к еще худшим последствиям.
Не очевидно и ты не описываешь почему. Значит догматик.
IB>Еще примеры неоправданности будут?
Я не могу показывать разницу между красным и синим слепому. Для тебя лично примеров новых не будет, ты не разобрался со старыми.
IB>Я не придумал, я подробно, и не раз, расписал почему так происходт, тебе лень прочитать?
Мне не лень я перечитал весь топик и ты ни разу не привёл конкретного примера.
A>> А тезис не спортный, библиотека одна, приложений много. IB>И какова при таком раскладе цена ошибки в библиотеке?
Дело не в цене ошибки, это ещё одна твоя абстрактная характеристика. Дело в том, что усложнение библиотеки дешевле усложнения приложений, посколько количество сложных мест уменьшается с N до 1, а значит усложнение библиотек оправданно по определению.
A>>Я хотел сказать что твой или ещё какой-то рейтинг принципиально ничуть не осмысленнее чем LOC/day и его область применения узка. IB>Ты уж определись, LOC — бессмысленен или узок? =)
Не перевирай чужие слова. "не осмысленнее чем LOC/day" и "бессмысленнее" это разные вещи. Учи русский язык. LOC может быть осмысленен только в очень узкой области и с большими оговорками.
A>>Глупости, связность никак не увеличилась, IB>Учи домашнее задание, тема — наследование..
Здравствуйте, AndrewVK, Вы писали:
A>>Сомнительно, я ведь использую все возможности языка, а ты помешался на конкретных. AVK>Рома, очень тебя прошу, заканчивай с психоанализом. Еще одной ветки про синглтон я не выдержу
Это та, где Бодягин утверждал, что количество результатов работы функции всегда равно количеству разных комбинаций значений её параметров? Да, да, я согласен, этот бред лучше не повторять.
Здравствуйте, adontz, Вы писали:
AVK>>Рома, очень тебя прошу, заканчивай с психоанализом. Еще одной ветки про синглтон я не выдержу
A>Это та, где Бодягин утверждал, что количество результатов работы функции всегда равно количеству разных комбинаций значений её параметров? Да, да, я согласен, этот бред лучше не повторять.
Главное предложение было первым.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
Z>Раз все идет хорошо, значит наш путь единственно верный?
Я где-то утверждал, что он единственно верный? Ты меня ни с кем не путаешь?
Z>Т.е. проблема порога решается просто обрубанием той части людей которые о него споткнулись.
Стадия изучения библиотеки должна быть в любом случае, вне зависимости от принципов на которых она построена.
Z>Походу мне попадались каждые первые, впрочем я не знаток явовских библиотек, можно ткнуть пальцем?
Любой IoC контейнер, коих в яве до попы.
Z>WCF тут непонятно каким боком. Где там преобладание статик хелперов?
Блинский фиг. Я не тебе что ли объяснял, и, как мне казалось, вроде бы сумел объяснить, что в 99% случаев статик хелперы превращаются в DI сервисы?
Z>Все что я помню из статиков в WCF является либо синглтоном либо фабрикой.
Да не причем тут статики. Статик хелпер — лишь один из вариантов выноса метода за пределы класса, опять-таки в принципе ни разу не оговорено куда ты все это будешь выносить.
Z>Можно не сдавая экзаменов по WCF (да и вообще первый раз увидев эту библиотеку) через 15 минут получить работающее сетевое приложение.
Правильно, благодаря грамотно составленной документации и куче ресурсов в инете. Так самый кривой на свете API можно заставить с демопримером работать.
Здравствуйте, Ziaw, Вы писали:
Z>Судя по ветке, не плюс явно не бесспорный.
Судя по ветке, как раз бесспорный, так как ни один из спорщиков это утверждение даже не попытался опровергнуть (как, впрочем и многие другие).
Z>Если бы они были, они бы имели доступ к приватным членам и ничем не отличались бы от обычного метода.
С какого перепугу?!они бы имели доступ к приватным переменным своего класса, но ни как не того класса, который расширяют.
Z>Если имеется ввиду расположение тестов — их можно располагать где угодно, если имеется нечто другое — не пойму что.
Если у тебя метод находится в классе и в своей реализации обращается к членам этого класса, то тестировать его можно только вмест с этим классом, и mock ты туде не подсунешь.
Здравствуйте, adontz, Вы писали:
A>Не очевидно
Очевидно.
A> и ты не описываешь почему.
Ты не потрудился прочитать?
A> Значит догматик.
"Рома — ты не адекватен" (с) IT =)
A>Я не могу показывать разницу между красным и синим слепому.
Я каждый раз сталкиваюсь с этой проблемой, когда по честному пытаюсь тебе что-то объяснить, уже зарекся..
A>Мне не лень я перечитал весь топик и ты ни разу не привёл конкретного примера.
Значит плохо читал, примеров было более чем достаточно.
A>Дело не в цене ошибки,
То есть, на ошибки библиотеки тебе наплевать? Очень удобная позиция. .
A>это ещё одна твоя абстрактная характеристика.
У меня все характеристики, как легко заметить, очень конкретные..
A>Не перевирай чужие слова.
Рома!! От тебя ли я это слышу??!
A>LOC может быть осмысленен только в очень узкой области и с большими оговорками.
То есть, Project Management — это узкая область? Нормально ты их приложил, молодец..
A>Я к тебе в класс не пойду, там скучно.
Так и останешься двоечником?..
Здравствуйте, adontz, Вы писали:
A>Сомнительно, я ведь использую все возможности языка,
Ты бессмыслено и беспощадно используешь все возможности языка.
А ведь некоторые говорят, что Nemerle безопасен, вот Роме бы я его не доверил, глядя что он с шарпом творит..
A>а ты помешался на конкретных.
Я просто использую их к месту.
Здравствуйте, stump, Вы писали:
S>Опять по существу ничего ответить не нашлось.
Я ответил именно по существу, в отличии, кстати, от тебя.
Я, конечно понимаю, что обидно признавать свою не правоту, но будь мужчиной, умей проигрывать.
S>Ну тогда тебе только и остается констатировать — все, что несоответствует правилу Бодягина есть демагогия.
Мне, конечно, очень лестно, что ты называешь это правило моим именем, но я не претендую на лавры Меерса, Саттера и Александреску.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, stump, Вы писали:
S>>Опять по существу ничего ответить не нашлось. IB>Я ответил именно по существу, в отличии, кстати, от тебя.
Ты последние два дня в этом топике только и делаешь что огрызаешься как затравленный, по существу от тебя давно уже ничего не слышно.
Но я тебе напомню, что я прочитал статью по ссылке и высказал две мысли по существу: (1) что правило Мейерса на которое ты ссылаешься сформулировано не так как его формулируешь ты, и (2) что выгоды которые описывает Мейерс актуальны для С++ и не актуальны для C#.
На это ты мне ответил по существу буквально следующее:
"Где ты категоричность нашел?"
"А кто говорил, что отменяет? Можно цитату?"
"Само правило вам не нравится, но логичных аргументов......"
В-общем:
Я, конечно понимаю, что обидно признавать свою не правоту, но будь мужчиной, умей проигрывать.
Z>>Судя по ветке, не плюс явно не бесспорный. IB>Судя по ветке, как раз бесспорный, так как ни один из спорщиков это утверждение даже не попытался опровергнуть (как, впрочем и многие другие).
Может и наличие инопланетян, призраков, ... бесспорный факт? Раз это никто не смог опровергнуть?
Здравствуйте, IB, Вы писали:
A>>Не очевидно IB>Очевидно.
Железная логика. Раз тебе показалось, что так оно и есть, значит это истина в последней инстанции. Мания величко страшная женщина.A>> и ты не описываешь почему.
A>>Мне не лень я перечитал весь топик и ты ни разу не привёл конкретного примера. IB>Значит плохо читал, примеров было более чем достаточно.
А ты дай ссылку на конкретный пример, конкретную проблему в рамках этого топика, авось отмоешься от звания теоретика-абстракциониста.
A>>Дело не в цене ошибки, IB>То есть, на ошибки библиотеки тебе наплевать? Очень удобная позиция. .
То есть я говорю о совсем другом, а ты в очередной раз переврал мои слова, так как по существу возразить тебе нечего.
A>>LOC может быть осмысленен только в очень узкой области и с большими оговорками. IB>То есть, Project Management — это узкая область? Нормально ты их приложил, молодец..
Если твой Project Management основывается на LOC, это очень многое о тебе говорит.
Здравствуйте, IB, Вы писали:
S>>Ну тогда тебе только и остается констатировать — все, что несоответствует правилу Бодягина есть демагогия. IB>Мне, конечно, очень лестно, что ты называешь это правило моим именем, но я не претендую на лавры Меерса, Саттера и Александреску.
Не надо так откровенно врать, это уже переходит всякие границы. Мейерс в примере в вомбатом прямо тебе противоречит. Это правило твоя личная инициатива и ни Мейерс, ни Саттер, ни александреску не имеют к нему ровным счётом никакого отношения.
Здравствуйте, Aikin, Вы писали:
A>Может и наличие инопланетян, призраков, ... бесспорный факт? Раз это никто не смог опровергнуть?
Нет, родной, если вести дискуссию в таком ключе, то вы вообще слили еще до ее начала.
Здравствуйте, adontz, Вы писали:
A>Железная логика.
Безусловно. Именно по этому тебе приходится прибегать к демагогии, в логике ты не силен..
A> Раз тебе показалось, что так оно и есть, значит это истина в последней инстанции.
Это тебе удобнее считать, что мне показалось, мне-то вообщем все равно, что показалось тебе..
A> и ты не описываешь почему.
Игнорировать аргументы — это очень удобная позиция, и вы, ребята, ее регулярно и с блеском демонстрируете. Проблема только в том, что она совершенно не конструктивна.
A>А ты дай ссылку на конкретный пример, конкретную проблему в рамках этого топика, авось отмоешься от звания теоретика-абстракциониста.
Мне-то отмываться неотчего, а вот тебе от репутации неадекватного склочника не отмыться уже никогда..
A>То есть я говорю о совсем другом,
Ты говоришь ровно об этом, все ходы записаны..
A>Если твой Project Management основывается на LOC, это очень многое о тебе говорит.
Где я писал, что основывается? Рома, не будь смешным..
Здравствуйте, adontz, Вы писали:
A> Мейерс в примере в вомбатом прямо тебе противоречит.
"Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода."
(c)
Здравствуйте, stump, Вы писали:
S>Ты последние два дня в этом топике только и делаешь что огрызаешься как затравленный,
Все попытки вести с тобой дискусии по существу закончились ровно тем, что ты обвинял оппонента в некорректном видении дискусии. И после этого я огрызаюсь?
Весь разговор перешел от собственно обсуждения правила, к обсуждению манеры ведения дискусии, при этом я-то как раз пытался до конца придерживаться темы топика, игнорируя откровенно хамские выпады. Тперь это называется огрызаться?
Забавно, буду знать. А как называется то, чем занимаешься ты?
S>по существу от тебя давно уже ничего не слышно.
По существу только от меня и слышно, вся ваша аргументация свелась к обвинению меня в догматизме.
S> что правило Мейерса на которое ты ссылаешься сформулировано не так как его формулируешь ты
Так тебе формулировка не нравится? На суть она не влияет.
S>, и (2) что выгоды которые описывает Мейерс актуальны для С++ и не актуальны для C#.
Выводы актуальные для C# я приводил не раз. По делу есть что возразить?
S>На это ты мне ответил по существу буквально следующее:
А вот это уже называется "художественная резьба по цитатам", и очередной пример демагогии. Мои ответы были не на замечания к ссылке, а на твои хамские выпады и передергивания, в чем легко убедиться просмотрев сообщение.
S>Я, конечно понимаю, что обидно признавать свою не правоту, но будь мужчиной, умей проигрывать.
Признаю. Состязание в демагогии и прикладном словоблудии я, видимо, проиграл. Я, в целом, не плохо развлекся, но в дальнейшем терять время будет обидно.
Здравствуйте, IB, Вы писали:
A>> Мейерс в примере в вомбатом прямо тебе противоречит. IB>"Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода." IB>Отсюда делаем вывод, что Рома не умеет читать.
Иван, для особо одарённых танкистов, ещё раз
в примере c вомбатом
Там явно описанно уже 100 раз мной упомянутое исключение. А выводе делай не раньше, чем научишься читать сообщения оппонента полностью.
Здравствуйте, IB, Вы писали:
A>>Железная логика. IB>Безусловно. Именно по этому тебе приходится прибегать к демагогии, в логике ты не силен..
Ещё одно перевирание моих слов, видимо это всё что тебе осталось. Ведь на то, что я говорю ответить нечего, значит надо отвечать на что-то другое.
A>> Раз тебе показалось, что так оно и есть, значит это истина в последней инстанции. IB>Это тебе удобнее считать, что мне показалось, мне-то вообщем все равно, что показалось тебе..
Ответ конкретный вопрос об исследованиях ты слил. У тебя нет экспериментальных данных, только догадки.
A>> и ты не описываешь почему. IB>Игнорировать аргументы — это очень удобная позиция, и вы, ребята, ее регулярно и с блеском демонстрируете. Проблема только в том, что она совершенно не конструктивна.
Факты, факты, где факты? У тебя только догадки.
A>>А ты дай ссылку на конкретный пример, конкретную проблему в рамках этого топика, авось отмоешься от звания теоретика-абстракциониста. IB>Мне-то отмываться неотчего, а вот тебе от репутации неадекватного склочника не отмыться уже никогда..
То есть ссылки на конкретный пример нет, Ч.Т.Д.
A>>То есть я говорю о совсем другом, IB>Ты говоришь ровно об этом, все ходы записаны.
Ту не умеешь читать. Я говорил о другом.
A>>Если твой Project Management основывается на LOC, это очень многое о тебе говорит. IB>Где я писал, что основывается? Рома, не будь смешным..
Действительно смешно пытаться что-то тебе объяснить. Ты не просто не прав, ты так самодоволлен что никогда в этом не признаешься, хотя тебе об этом говорит множество людей. Даже Мейерс, на которого ты ссылаешься, и то никогда не говорил того, что ты ему приписываешься. Это твоя позиция, придумать себе врага и бороться с ним, вместо того чтобы хоть раззок послушать тех, кто умнее.
Здравствуйте, adontz, Вы писали:
A>Иван, для особо одарённых танкистов, ещё раз
Для особо одаренных танкистов переформулирую: "Рома не умеет читать до конца" (с)
Если Вы самокритичны и честны сами с собой, вы увидите, что имеете эту предполагаемую несогласованность со всеми нетривиальными классами, Вы используете ее потому, что класс не может имеет любую функцию, которую пожелает како-то клиент.
Разжевываю: Меерс говорит о том, что как бы ты не пытался впихнуть все методы в класс сразу, все равно, рано или поздно появится новая функция, когда класс уже будет поздно менять. И она полюбому окажется снаружи, и так тщательно взлелеянная красивость, достигнутая уменьшением качества поддержки, все равно развалится.
Собственно, Андрей специально для тебя это уже цитировал.
A>А выводе делай не раньше, чем научишься читать сообщения оппонента полностью.
Я бы сократил и абстрагировал.. — "А выводе делай не раньше, чем научишься читать ... полностью." =)
Здравствуйте, adontz, Вы писали:
A>Ещё одно перевирание моих слов,
Рома, где я переврал твои слова?! =)
Ты выдвинул тезис, я с ним полностью согласился! Или у тебя уже рефлекс, автоматом возражать на любую мою фразу?
A>Ответ конкретный вопрос об исследованиях ты слил.
О каких исследованиях?
A>Факты, факты, где факты? У тебя только догадки.
Фактов — полная тема, читай, Рома, читай..
A>То есть ссылки на конкретный пример нет, Ч.Т.Д.
Все есть в топике — дерзай..
A>Ту не умеешь читать. Я говорил о другом.
"Все ходы записаны".. (с)
A> Это твоя позиция, придумать себе врага и бороться с ним, вместо того чтобы хоть раззок послушать тех, кто умнее.
Рома, не занимайся публичным самоанализом, выглядит забавно, но твое душевное здоровье несколько тревожит.
Ну или, в конце-концов, прислушайся к своим словам..
Здравствуйте, IB, Вы писали:
IB>Для особо одаренных танкистов переформулирую: "Рома не умеет читать до конца" (с) IB>
IB>Если Вы самокритичны и честны сами с собой, вы увидите, что имеете эту предполагаемую несогласованность со всеми нетривиальными классами, Вы используете ее потому, что класс не может имеет любую функцию, которую пожелает како-то клиент.
IB>Разжевываю: Меерс говорит о том, что как бы ты не пытался впихнуть все методы в класс сразу, все равно, рано или поздно появится новая функция, когда класс уже будет поздно менять. И она полюбому окажется снаружи, и так тщательно взлелеянная красивость, достигнутая уменьшением качества поддержки, все равно развалится.
Опять переврал, специально для тебя пришлось найти и перевести текст полностью.
Здравствуйте, IB, Вы писали:
IB>Ты выдвинул тезис, я с ним полностью согласился!
Вот наш диалог
adontz> А тебе никто не приведёт пример, когда правило не получается применить. Его получается применить всегда, однако, время от времени, это приводит к абсурдному результату, когда логически связанная группа методов оказывается разбросана по разным классам.
IB> Ты нашел один пример, в котором, по твоему мнению, применение правила не оправдано. Однако, очевидно, его не применение приводит к еще худшим последствиям. Значит применение по прежнему считаем оправданым.
adontz> Не очевидно и ты не описываешь почему.
IB> Очевидно.
adontz> Железная логика. Раз тебе показалось, что так оно и есть, значит это истина в последней инстанции.
IB> Безусловно. Именно по этому тебе приходится прибегать к демагогии, в логике ты не силен.
adontz> Ещё одно перевирание моих слов, видимо это всё что тебе осталось. Ведь на то, что я говорю ответить нечего, значит надо отвечать на что-то другое.
IB> Рома, где я переврал твои слова?! =)
IB> Ты выдвинул тезис, я с ним полностью согласился!
Я так понял, ты согласен с тезисом, что LeftSubstring и RightSubstring, а так же все другие логические группы методов не должны раскидываться по классам в угоду инкапсуляции. Очень хорошо, лучше поздно, чем никогда.
A>>Ответ конкретный вопрос об исследованиях ты слил. IB>О каких исследованиях?
Ты неоднократно утверждал, что если не выделять методы в хелперы, то будет больше багов. Тебя неоднократно и не только я просили привести факты подтверждающие эту теорию.
A>>То есть ссылки на конкретный пример нет, Ч.Т.Д. IB>Все есть в топике — дерзай..
Здравствуйте, AndrewVK, Вы писали:
AVK>Не будешь ли ты любезен привести конкретную цитату, где Мейерс говорит, что в примере с вомбатом следует поместить функцию в класс вомбат?
If you're like many people with whom I've discussed this issue, you're likely to have reservations about the syntactic implications of my advice that non-friend non-member functions should be preferred to member functions, even if you buy my argument about encapsulation.
Если вы такие же, как и многие люди с которыми я обсуждал эту проблему, у вас будут возражения по поводу синтаксических последствий моего предложения, что недружественные функции не члены должны предпочитаться методам, даже если вы согласны с аргументацией касательно инкапсуляции.
For example, suppose a class Wombat supports the functionality of both eating and sleeping. Further suppose that the eating functionality must be implemented as a member function, but the sleeping functionality could be implemented as a member or as a non-friend non-member function. If you follow my advice from above, you'd declare things like this:
Например, представим класс Wombat поддерживает функциональность поедания и сна. Так же предположим, что функциональность поедания должна быть реализованна как метод, но функциональность сна может быть реализованна как метод или недружественная функция не член. Если вы последуете моему предложению, вы опишете всё нижеследующим образом:
Итак, специально для Бодягина, здесь Мейерс явно указывает что синтаксическая согласованность это прямой повод для отступления от его же правила. Это ведь всего лишь правило и его нельзя применять всегда. Он не такой фанатик как товарищь Бодягин, что делает честь ему и не делает чести сами понимаете кому
Далее
But this uniformity is misleading, because there are more functions in the world than are dreamt of by your philosophy.
Однако, эта согласованность обманчива, так как функций в мире больше, чем вы можете себе представить.
To put it bluntly, non-member functions happen. Let us continue with the Wombat example. Suppose you write software to model these fetching creatures, and imagine that one of the things you frequently need your Wombats to do is sleep for precisely half an hour.
Скажем прямо, функции не-члены приходится писать. Давайте продолжим с примером о классе Wombat. Предположим вы создаёте программную модель ловли животных и представим, что одно из часто необходимых вам действий это усыпить вомбата ровно на пол-часа.
Тут надо пояснить пример, чего Мейерс к сожалению не делает. Это не пример разработки класса Wombat, это пример его использования. Необходимое действие, получасовой сон, является характерным не для класса Wombat, а для пользователя класса Wombat.
Clearly, you could litter your code with calls to w.sleep(.5), but that would be a lot of .5s to type, and at any rate, what if that magic value were to change? There are a number of ways to deal with this issue, but perhaps the simplest is to define a function that encapsulates the details of what you want to do. Assuming you're not the author of Wombat, the function will necessarily have to be a non-member, and you'll have to call it as such:
Очевидно, вы можете разбросать по своему коду вызовы w.sleep(.5), но вам придётся напечатать очень много 0.5, и как бы быстро вы это ни делали, что если магическая константа изменится? Есть мнодество решений данной проблемы, но, наверное, наипростейшее — это определить функцию скрывающую детали реализации. Предполагая, что вы не автор класса Wombat, функция может обязательно будет не членом и вам придётся вызывать её так:
// might be inline, but it doesn't mattervoid nap(Wombat& w) { w.sleep(.5); }
Wombat w;
...
nap(w);
Из фразы "Assuming you're not the author of Wombat" становиться совершенно очевидной область применения внешних манипуляторов — это упрощение использованияя класса, но никак не его разработка.
And there you have it, your dreaded syntactic inconsistency. When you want to feed your wombats, you make member function calls, but when you want them to nap, you make non-member calls.
И вот вы это получили, ваш кошмар — синтаксическая несогласованность. когда вы хотите покормить вомбатов, вы вызываете метод, но когда хотите заставить их подремать, вы вызываете функцию не член.
If you reflect a bit and are honest with yourself, you'll admit that you have this alleged inconsistency with all the nontrivial classes you use, because no class has every function desired by every client. Every client adds at least a few convenience functions of their own, and these functions are always non-members.
Если Вы самокритичны и честны сами с собой, вы увидите, что имеете эту предполагаемую несогласованность со всеми нетривиальными классами, Вы используете ее потому, что класс не может имеет любую функцию, которую пожелает какой-то пользователь. Каждый пользователь добавляет несколько функций для своего удобства, и эту функции всегда не члены.
Собственно, вот теперь уже абсолютно ясно, что внешние манипуляторы это не манна небесная, а неприятная необходимость, неудобный выход для пользователей класса. И задача extension methods дать пользователям класса возможность расширять классы не вызывая синтаксической несогласованности.
Собственно далее, Мейерс расставляет все точки над i, полностью превознося синстаксическую согласованность над инкапсуляцией: "People just look up which syntax is appropriate for the functions they want to call, then they call them".
Я на 100% уверен, что и этот полный текст, Бодягин расщепит на несколько малоосмысленных предложений и истолкует их как-то по своему, лишь бы не признать, что дядюшки Мейерса у него за спиной нет и "правило Бодягина", названо так не для признания заслуг, а потому что только он им и пользуется.
Понятно, история с синглтоном повторяется: смотрим в книгу видим фигу. Продолжайте без меня.
P.S. "To put it bluntly, non-member functions happen." переводится как "Попросту говоря, функции нечлены встречаются.". И "скажем прямо", ни "приходится" в этой фразе отсуствуют.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
[не относится к теме, поскипано...]
S>> что правило Мейерса на которое ты ссылаешься сформулировано не так как его формулируешь ты IB>Так тебе формулировка не нравится? На суть она не влияет.
Приехали. Формулировка не влияет на суть. Видимо по тому что сути нет никакой.
Заметь, ведь никто не спорит с тобой о том, что функции работающие с публичным контрактом класса можно выносить за пределы класса. Спор идет о том что "правило Бодягина" утверждает, что это нужно делать всегда, независимо не от каких условий. Но почему это надо делать всегда, ты толком объяснить не можешь.
Тот же Мейерс делает исключения, для случая когда необходимо виртуальное поведение, и для целого ряда случаев специфических для C++.
AndrewVK делает исключение, что в случае когда важна производительность, можно поступится правилом и перенести функцию в класс чтобы она смогла работать с приватными данными.
Другие коллеги справедливо замечают, что в большинстве используемых библиотек рассматриваемое правило не соблюдается, и находят этому различные объяснения.
Но ты продолжаешь упроствовать в этом своем "всегда", теперь уже даже не утруждаясь аргументировать свою позицию
S>>, и (2) что выгоды которые описывает Мейерс актуальны для С++ и не актуальны для C#. IB>Выводы актуальные для C# я приводил не раз. По делу есть что возразить?
А у тебя?
Здравствуйте, stump, Вы писали:
S>AndrewVK делает исключение, что в случае когда важна производительность, можно поступится правилом и перенести функцию в класс чтобы она смогла работать с приватными данными.
Это не исключение. Если функция должна работать с приватными данными, неважно по какой причине, значит функцию вынести не получится. Все в точном соответствии с предложенным принципом.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Это не исключение. Если функция должна работать с приватными данными, неважно по какой причине, значит функцию вынести не получится. Все в точном соответствии с предложенным принципом.
Крайне несостоянтельный у тебя получился ответ, ведь можно сказать, что функция должна работать с приватными данными, потому что все другие функции класса работают с приватными данными. Это, Андрей, не что-нибудь, а логика! Была проведена аналогия! Это такой метод логического мышления.
Здравствуйте, adontz, Вы писали:
A>Крайне несостоянтельный у тебя получился ответ, ведь можно сказать, что функция должна работать с приватными данными, потому что все другие функции класса работают с приватными данными.
Нельзя.
A> Это, Андрей, не что-нибудь, а логика!
Нет, на логику это совсем не похоже.
A> Была проведена аналогия! Это такой метод логического мышления.
Ну если это твой метод логического мышления, тогда все понятно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
A>> Это, Андрей, не что-нибудь, а логика! AVK>Нет, на логику это совсем не похоже.
На самом деле это очень даже логика, просто неуместная. Ничего, скоро ты тоже научишься различать.
A>> Была проведена аналогия! Это такой метод логического мышления. AVK>Ну если это твой метод логического мышления, тогда все понятно.
Нет, это не мой метод, это шутка. Я специально поставил три смайлика для людей без чувства юмора чтобы и им было понятно. Видимо, надо было поставить пять. В следующий раз так и поступлю. А может поэксперементировать с чётными числами, что думаешь?
На основной вопрос "Кто и как пределяет это самое "должна"?" есть что ответить или опять к деталям будешь придираться?
Здравствуйте, adontz, Вы писали:
A>На самом деле это очень даже логика, просто неуместная. Ничего, скоро ты тоже научишься различать.
Не, не научусь.
AVK>>Ну если это твой метод логического мышления, тогда все понятно.
A>Нет, это не мой метод, это шутка. Я специально поставил три смайлика для людей без чувства юмора чтобы и им было понятно. Видимо, надо было поставить пять. В следующий раз так и поступлю. А может поэксперементировать с чётными числами, что думаешь?
Лучше, если уж отметил завершение рабочей недели, не пиши в форум.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
>> Если функция должна работать с приватными данными, неважно по какой причине, значит функцию вынести не получится. Все в точном соответствии с предложенным принципом.
Видимо у тебя проблемы со зрением и ты не разглядел мой вопрос. Попробуем иначе.
Здравствуйте, adontz, Вы писали:
A>Я так понял, ты согласен с тезисом, что LeftSubstring и RightSubstring,
Я уже тебе говорил, что ты не умеешь читать. .
Ты выдвинул тезис, что у меня железная логика и на это утверждение мне совершенно нечего возразить.. =)
A>Ты неоднократно утверждал, что если не выделять методы в хелперы, то будет больше багов.
Утверждал и даже объяснял почему, не однократно.
A> Тебя неоднократно и не только я просили привести факты подтверждающие эту теорию.
Почитай топик, там все есть.. =)
A>То есть сам указать не можешь. Ч.Т.Д.
Я уже указал, и не однократно. Я что, для каждого ленивого должен заново все повторять?
Здравствуйте, stump, Вы писали:
S>Приехали. Формулировка не влияет на суть. Видимо по тому что сути нет никакой.
Опять хамить начинаешь? Здравый смысл должен был подсказать, что одно и то же утверждение можно сформулировать по разному, и суть утверждения от этого не изменится.
S>Заметь, ведь никто не спорит с тобой о том, что функции работающие с публичным контрактом класса можно выносить за пределы класса.
Я уже ничему не удивлюсь.
S> Но почему это надо делать всегда, ты толком объяснить не можешь.
Да куда уж толковей-то?! Я потратил кучу времени, приводя и теоретические выкладки и различные сценарии и поясняя, почему в данном конкретном случае лучше выносить — не читал? Или что-то было не понятно? Тогда надо было просто спросить, я бы объяснил.
S>Тот же Мейерс делает исключения, для случая когда необходимо виртуальное поведение, и для целого ряда случаев специфических для C++.
В том-то и дело, что для C++, для C# вы так и не смогли привести ни одного убедительного сценария.
S>AndrewVK делает исключение, что в случае когда важна производительность, можно поступится правилом и перенести функцию в класс чтобы она смогла работать с приватными данными.
Ты так до сих пор и не удосужился понять, как работает правило? О чем мы тут тогда вообще разговариваем?
Так. Еще раз. Просто чтобы меня опять не обвинили в том, что я ухожу от разговора. Все построено очень просто. Дизайн функции делается сначала, правило не диктует — должна функция пользоваться приватными переменными или нет. Правило лишь определяет где ей находиться, после того как ее спроектировали. Таким образом, это не исключение. Это понятно?
S>Другие коллеги справедливо замечают, что в большинстве используемых библиотек рассматриваемое правило не соблюдается, и находят этому различные объяснения.
"Большинство" — это FCL? Объяснение тоже было одно, так что не надо ненужных гипербол.
Кроме того, во-первых, кривой дизайн конкретной библиотеки не означает, что и все остальные надо так же уродовать, а во вторых, есть масса обратных примеров.
S>Но ты продолжаешь упроствовать в этом своем "всегда",
Упорствовать продолжаете вы. Причем я уже даже не понимаю в чем. У меня складывается очень четкое ощущение, что у вас уже чисто принципиальное желание, поймать меня уже хоть на чем-нибудь.
S>теперь уже даже не утруждаясь аргументировать свою позицию
Сколько можно выдвигать новые и повторять старые аргументы, пытаясь построить их так чтобы дошло, если их просто игнорируют? Аргументов было больше чем достаточно.
Вынужден констатировать, что и с упертостью борьбу я так же проиграл, мне уже стало не интересно.
По началу было, конечно, забавно ловить тебя на неуклюжих попытках интерпретировать все в свою пользу (чего, честно говоря не ожидал), но потом стало утомлять.
Yess!! Черт возьми, когда появляется персональный клоун, это, пожалуй, действительно слава.
A>Ah, the uniformity of it all! A>Согласованность во всём!
Ром, ты здесь правда иронию не уловил или тебя просто что-то вштырило? =)
A>Собственно далее, Мейерс расставляет все точки над i, полностью превознося синстаксическую согласованность над инкапсуляцией: "People just look up which syntax is appropriate for the functions they want to call, then they call them".
Ром, отсыпь пожалуйста, я тоже так хочу.. )
У меня просто не получается настолько изменить сознание, чтобы фразу о том, что людям пофиг как вызвается функция, интерпретировать как пропаганду синтаксической согласованности.. =)
Это у вас там что-то местное?
Здравствуйте, adontz, Вы писали:
A>Опять переврал,
Конкретно, что именно я переврал? Вот можешь прям взять мой текст, взять оригинал и точно указать, где и что я "переврал", по сути, конечно, на буквальный перевод я не претендую.
A>специально для тебя пришлось найти и перевести текст полностью.
На пользу тебе это, судя по всему, не пошло.
Здравствуйте, IB, Вы писали:
IB>Да куда уж толковей-то?! Я потратил кучу времени, приводя и теоретические выкладки и различные сценарии и поясняя, почему в данном конкретном случае лучше выносить — не читал? Или что-то было не понятно? Тогда надо было просто спросить, я бы объяснил.
Ключевое слово выделено. Практики от тебя не было, ты на каждый конкретный вопрос только отвечаешь "я уже писал, вы не умеете читать".
IB>"Большинство" — это FCL? Объяснение тоже было одно, так что не надо ненужных гипербол. IB>Кроме того, во-первых, кривой дизайн конкретной библиотеки не означает, что и все остальные надо так же уродовать, а во вторых, есть масса обратных примеров.
Ты так и не объяснил в чем же такой кривой FCL и конкретно, например, класс String. Да, давай напиши что ты уже писал, конкретики от тебя не будет никогда.
Здравствуйте, IB, Вы писали:
IB>Ром, отсыпь пожалуйста, я тоже так хочу.. )
Лучше ты отсыпь, наверное тогда тоже будут глюки якобы всем всё конкретно объяснил, а они не умеют читать.
Здравствуйте, MozgC, Вы писали:
MC>Практики от тебя не было, ты на каждый конкретный вопрос только отвечаешь "я уже писал, вы не умеете читать".
На каждый действительно конкретный вопрос, был дан конкретный ответ. Я человек ленивый, и повторять дважды мне совершенно не интересно, особенно для товарищей, которые даже не читают — не вижу смысла.
MC>Ты так и не объяснил в чем же такой кривой FCL и конкретно, например, класс String.
Если ты не в состоянии понять, что, скажем, пример с классом Rectangle ничем не отличается от примера с классом String, то тебе уже ничто не поможет. Тем более не вижу смысла тратить усилия.
А то в библиотек много, классов еще больше — ты мне будешь приплачивать за подробное объяснение по каждому?
MC>Да, давай напиши что ты уже писал, конкретики от тебя не будет никогда.
My favorite:
- Иногда письма не доходят.
— Да, бывалыча пишешь письмо, пишешь, получаешь ответ и понимаешь — не дошло письмо, не дошло..."
Здравствуйте, MozgC, Вы писали:
MC>Лучше ты отсыпь, наверное тогда тоже будут глюки якобы всем всё конкретно объяснил, а они не умеют читать.
Нет, вот твоя трава мне не нравится, она тупая, занудная и на зрение влияет.
Здравствуйте, Cyberax, Вы писали:
C>Кстати, а кто автор этого высказывания? Я где-то это видел (в ФИДО?) но не могу вспомнить.
Да, это было в ФИДО в humor.filtered =)
Автора, увы, не помню, хотя если покопаться в архивах, возможно получится найти..
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, MozgC, Вы писали:
MC>>Лучше ты отсыпь, наверное тогда тоже будут глюки якобы всем всё конкретно объяснил, а они не умеют читать. IB>Нет, вот твоя трава мне не нравится, она тупая, занудная и на зрение влияет.
Я тебе не предлагал, да у меня и нет. Ты опять тупишь и не в тему отвечаешь.
Предлагаю рассмотреть имплементацию класса String для работы строк .
Во первых и пользы будет больше и конкретики и может понимания.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[21]: Куда девать ф-ции внешние для класса
От:
Аноним
Дата:
26.07.08 18:48
Оценка:
Давайте вернемся к примеру о котором написал adontz. Есть наш класс String. У него есть методы LeftSubstring() и RightSubstring(). LeftSubstring() по правилу Бодягина оставлен в классе, т.к. использует внутренние данные класса, а метод RightSubstring() вынесен во внешний сервис.
class String
{
public String LeftSubstring(int symbolCount);
}
class StringService
{
public static String RightSubstring(String value, int symbolCount);
}
По правилу Бодягина это правильно и нормально. Правильно ли я в данном случае применил правило или вы хотите что-то уточнить?
Здравствуйте, IB, Вы писали:
IB>Вынужден констатировать, что и с упертостью борьбу я так же проиграл, мне уже стало не интересно. IB>По началу было, конечно, забавно ловить тебя на неуклюжих попытках интерпретировать все в свою пользу (чего, честно говоря не ожидал), но потом стало утомлять.
Да не кипятись ты так. Ведь тебя никто не заставляет отрекаться от твоего любимого правила проектирования, как инквизиция Галилео Галилея. А обращать неразумных крестом и мечом в собственную веру тоже не очень разумно.
Ты никогда не сталкивался с ситуацией когда есть успешный продукт (проект), а когда заглянешь в код, а там б^@ такое внутри... А код то работает.
В общем "Hakuna Matata".
Здравствуйте, <Аноним>, Вы писали:
А>Давайте вернемся к примеру о котором написал adontz. Есть наш класс String. У него есть методы LeftSubstring() и RightSubstring(). LeftSubstring() по правилу Бодягина оставлен в классе, т.к. использует внутренние данные класса, а метод RightSubstring() вынесен во внешний сервис.
Если тебе очень хочется обсудить конкретный пример, то ответ простой. Пример абсурден. Нет ни одного разумного обоснования, почему вдруг LeftSubstring нужен доступ к внутренним членам класса, а RightSubstring не нужен. В реальных строках, из-за жесткого требования к производительности, обе этих функции потребуют доступ к внутреннему буферу строки.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, stump, Вы писали:
S>Ты никогда не сталкивался с ситуацией когда есть успешный продукт (проект), а когда заглянешь в код, а там б^@ такое внутри... А код то работает.
И какой из этого следует вывод? Можно не тратить силы на проектирование, и писать так как показалось на первый взгляд удобнее?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
А>>Давайте вернемся к примеру о котором написал adontz. Есть наш класс String. У него есть методы LeftSubstring() и RightSubstring(). LeftSubstring() по правилу Бодягина оставлен в классе, т.к. использует внутренние данные класса, а метод RightSubstring() вынесен во внешний сервис.
AVK>Если тебе очень хочется обсудить конкретный пример, то ответ простой. Пример абсурден. Нет ни одного разумного обоснования, почему вдруг LeftSubstring нужен доступ к внутренним членам класса, а RightSubstring не нужен. В реальных строках, из-за жесткого требования к производительности, обе этих функции потребуют доступ к внутреннему буферу строки.
AndrewVK, вместо решения конкретной решения задачи ты решил повоевать с её условиями? Я могу конкретизировать задачу примером кода. Надеюсь, теперь я услышу однозначный ответ, а то мне на одно мгновение показалось, что у тебя решения задачи нет, и в связи с этим тебе хочется выставить её абсурдной.
using System;
namespace AndrewVK_String
{
public class MyString
{
private char[] _data;
private int _length;
public MyString(char[] data)
{
this._data = data;
this._length = data.Length;
}
public MyString(char[] data, int length)
{
this._data = data;
this._length = length;
}
public MyString LeftSubstring(int symbolCount)
{
return new MyString(this._data, symbolCount);
}
public MyString MiddleSubstring(int offset, int symbolCount)
{
char[] data = new char[symbolCount];
Array.Copy(this._data, offset, data, 0, symbolCount);
return new MyString(data, symbolCount);
}
public int Length
{
get { return this._length; }
}
}
public class MyStringHelper
{
public static MyString RightSubstring(MyString value, int symbolCount)
{
return value.MiddleSubstring(value.Length - symbolCount, symbolCount);
}
}
public class Program
{
public static void Main(string[] args)
{
MyString str = new MyString(new char[] { 'a', 'b', 'c', 'd' });
MyString left = str.LeftSubstring(3);
MyString right = MyStringHelper.RightSubstring(str, 3);
}
}
}
Здравствуйте, MozgC, Вы писали:
MC>AndrewVK, вместо решения конкретной решения задачи ты решил повоевать с её условиями
Понимаешь, если выдумывать бредовые условия, то решения тоже будут бредовыми. Garbage in, garbage out.
MC>Я могу конкретизировать задачу примером кода.
Пример кода тоже бредовый.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
MC>>Я могу конкретизировать задачу примером кода. AVK>Пример кода тоже бредовый.
Можешь привести пример не бредового кода? А именно, реализацию методов LeftSubstring(), MiddleSubstring() и RightSubstring(), оперирующих с полями
private char[] _data;
private int _length;
Насколько я тебя понимаю, при правильной реализации этих методов (моя неправильная), описанной проблемы не возникает, и все три метода окажутся либо вне класса, либо внутри, причём, совершенно естественным образом. Очень прошу не полениться, не ссылаться на очевидность и всё же привести свой вариант исходного кода. Не думаю, что написание 20 простейших строк отнимет много времени.
Здравствуйте, MozgC, Вы писали:
MC>Можешь привести пример не бредового кода?
А зачем?
MC>Насколько я тебя понимаю, при правильной реализации этих методов (моя неправильная), описанной проблемы не возникает, и все три метода окажутся либо вне класса, либо внутри, причём, совершенно естественным образом.
Точно.
MC> Очень прошу не полениться, не ссылаться на очевидность и всё же привести свой вариант исходного кода. Не думаю, что написание 20 простейших строк отнимет много времени.
Приведу, как только пойму смысл сего деяния.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
MC>>Можешь привести пример не бредового кода? AVK>А зачем?
Во мне закралось сомнение в правильности моей позиции. Если ты не против поделиться знаниями и опытом, то исходный код был бы очень уместным учебным материалом и убедительным аргументом. Ты ведь не против поделиться знаниями и опытом, не так ли? Что тебе стоит привести правильный пример, в котором описанной выше проблемы не существует? Неужели это так сложно, что нужна какая-то особая мотивация, большая чем для всех тех сообщений, которые ты уже написал?
Здравствуйте, MozgC, Вы писали:
MC>Во мне закралось сомнение в правильности моей позиции. Если ты не против поделиться знаниями и опытом, то исходный код был бы очень уместным учебным материалом и убедительным аргументом. Ты ведь не против поделиться знаниями и опытом, не так ли? Что тебе стоит привести правильный пример, в котором описанной выше проблемы не существует? Неужели это так сложно, что нужна какая-то особая мотивация, большая чем для всех тех сообщений, которые ты уже написал?
Я пока что так и не понимаю, зачем тебе код. Очевидно, что самая эффективная реализация всех вариаций сабстрингов — прямое копирование нужного куска буфера в новую строку. И либо ты это копируешь в каждой вариации своих XxxSubstring, либо, если уж делаешь конструктор, то делай так чтобы можно было указывать startIndex, а не только диапазон, код от этого ни капельки не усложнится. И при таком раскладе все XxxSusbstring стоит сделать ввиде extension методов. В твоем же варианте какая то абсурдная смесь. Единственный смысл такого кода я пока вижу лишь в том, чтобы доказать свою правоту.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
MC>>Во мне закралось сомнение в правильности моей позиции. Если ты не против поделиться знаниями и опытом, то исходный код был бы очень уместным учебным материалом и убедительным аргументом. Ты ведь не против поделиться знаниями и опытом, не так ли? Что тебе стоит привести правильный пример, в котором описанной выше проблемы не существует? Неужели это так сложно, что нужна какая-то особая мотивация, большая чем для всех тех сообщений, которые ты уже написал? AVK>Я пока что так и не понимаю, зачем тебе код.
В данном топике было очень много недопонимания, из-за того что разные люди по разному додумывали наброски чужих решений. Код исключил бы элемент двусмысленности. Ты можешь его предоставить?
Здравствуйте, MozgC, Вы писали:
MC>В данном топике было очень много недопонимания, из-за того что разные люди по разному додумывали наброски чужих решений. Код исключил бы элемент двусмысленности.
Ага, зато код гарантированно переведет обсуждение из обсуждения принципа в обсуждение конкретного кода. Ты этого добиваешься?
MC> Ты можешь его предоставить?
На всякий случай, если вдруг ты начнешь говорить, что твоя реализация быстрее — она никуда не годится, так как без копирования массива в конструкторе у тебя не обеспечивается имьютабельность. В реальной нетовской строке такое копирование происходит.
public class String1
{
private readonly char[] _data;
public String1(char[] data, int startIndex, int length)
{
_data = new char[length];
Array.Copy(data, startIndex, _data, 0, length);
}
public String1 Substring(int startIndex, int length)
{
return new String1(_data, startIndex, length);
}
public int Length
{
get { return _data.Length; }
}
}
public static class String1Extensions
{
public static String1 LeftString(this String1 str, int length)
{
return str.Substring(0, length);
}
public static String1 RightString(this String1 str, int length)
{
return str.Substring(str.Length - length, length);
}
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, <Аноним>, Вы писали:
А>Давайте вернемся к примеру о котором написал adontz. Есть наш класс String. У него есть методы LeftSubstring() и RightSubstring(). LeftSubstring() по правилу Бодягина оставлен в классе, т.к. использует внутренние данные класса, а метод RightSubstring() вынесен во внешний сервис.
А>
class String
А>{
А> public String LeftSubstring(int symbolCount);
А>}
А>class StringService
А>{
А> public static String RightSubstring(String value, int symbolCount);
А>}
Начнем с того что я не понимаю чем могут такие методы отличаться. Я бы не парился и предоставил одну функциональность
public String LeftSubstring(String& from, int begin, int end);
Если же реализация подразумевает только создание двух типов подстринга тогда я бы действовал следующим образом.
1. Рассмотрел бы подробно создание нового класса String , может создание подстринга можно запихнуть в конструктор.
2. Рассмотрел бы эфективность создания с помощью публичных контрактов.
3. Один можно публичным а другой нет , долго бы думал о внесении первого в метод класса.
4. При вынесении создания в функцию или класс хелпер , пострался бы не давать функциональности похожие имена , чтобы на интуитивном уровне воспринималось отличие этих методов.
А в целом практически бесполезно говорить про дизайн класса пока не расмотрена его ответственность и не определенны методы сценарии использования.
А>По правилу Бодягина это правильно и нормально. Правильно ли я в данном случае применил правило или вы хотите что-то уточнить?
Понятия не имею , вам решать если вы знаете правило бодягина.
Если согласиться с реализацией , то вынос функции в класс хелпер выглядит довольно разумно , он явно не необходимая часть класса и работает именно как хелпер поверх реализации.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, minorlogic, Вы писали:
M>>MiddleSubstring забыл
AVK>Оно чем то должно отличаться от моего Substring?
Здравствуйте, Ziaw, Вы писали:
IB>>2. Все побочные эффекты изменения реализаций контролирует компилятор. Z>Может стоит ввести в язык новый модификатор метода который имеет доступ только к публичным членам?
А можно класс разбить на несколько сущностей внутри имплементации. Минимальный набор функциональности прячем вглубь, остальное реализуем публичным интерфейсом. Как вариант.
Здравствуйте, stump, Вы писали:
S> А обращать неразумных крестом и мечом в собственную веру тоже не очень разумно.
Да я все больше на разумных рассчитывал...
S>Ты никогда не сталкивался с ситуацией когда есть успешный продукт (проект), а когда заглянешь в код, а там б^@ такое внутри... А код то работает.
Ок, хорошая отмазка, слив засчитан..
Здравствуйте, IB, Вы писали:
IB>Судя по ветке, как раз бесспорный, так как ни один из спорщиков это утверждение даже не попытался опровергнуть (как, впрочем и многие другие).
Предлагаю закрыть спор об очевидности, как очевидно бесплодный.
IB>С какого перепугу?!они бы имели доступ к приватным переменным своего класса, но ни как не того класса, который расширяют.
Если они в другом классе — они никак могут быть заменой методам с ограниченным доступом к собственным мемберам.
IB>Если у тебя метод находится в классе и в своей реализации обращается к членам этого класса, то тестировать его можно только вмест с этим классом, и mock ты туде не подсунешь.
Почему вдруг? Никаких проблем не вижу. Хочу пример, как вынос метода (только вынос, без других телодвижений) помог что-то оттестировать.
Здравствуйте, IB, Вы писали:
IB>Блинский фиг. Я не тебе что ли объяснял, и, как мне казалось, вроде бы сумел объяснить, что в 99% случаев статик хелперы превращаются в DI сервисы?
Спор сейчас о другом: стоит ли выносить в хелперы или DI сервисы все методы которые туда вынести возможно.
Про применимость DI сервиса или стратегии я бы не стал спорить вообще. Я не согласен с тем, что во всех местах где это позволяет производительность и инкапсуляция метод должен быть вынесен.
В первую очередь, вынос метода не должен снижать удобство работы с модулем.
ответил
AVK>Это не тот порог. Чтобы написать простенький код — да, скопировал пример и вперед. А вот если нужно понимать, что происходит (а это обязательное условие, если ты это применяешь в серьезном проекте) — вот тут все становится сразу печально.
Это сложность предметной области. WCF отлично ее скрывает, оставляя при этом множество механизмов тонкой настройки под себя. Что говорит об отличном дизайне. Проектировалась она явно с упором на юзабилити, а не на стоимость поддержки кода. Впрочем я до сих пор не убежден, что поголовный вынос всех возможных методов вон из класса снижает стоимость поддержки.
Здравствуйте, Ziaw, Вы писали:
Z>Спор сейчас о другом: стоит ли выносить в хелперы или DI сервисы все методы которые туда вынести возможно.
Вот именно, а ты прицепился что статик хелперы плохи. Не нравятся хелперы, выноси в DI, локатор или еще какой сервис — не принципиально.
Z> Я не согласен с тем, что во всех местах где это позволяет производительность и инкапсуляция метод должен быть вынесен.
Ты утверждаешь, что он должен быть оставлен, если так его "удобнее" использовать? Но рано или поздно все равно появится функционал, который из соображений "удобства" надо вкрячить в класс, а делать это уже будет поздно. И за что боролись?
Z>В первую очередь, вынос метода не должен снижать удобство работы с модулем.
В первую очередь дизайн модуля должен облегчать его поддержку и снижать вероятность ошибок при внесении изменений.
Здравствуйте, Ziaw, Вы писали:
AVK>>Это не тот порог. Чтобы написать простенький код — да, скопировал пример и вперед. А вот если нужно понимать, что происходит (а это обязательное условие, если ты это применяешь в серьезном проекте) — вот тут все становится сразу печально. Z>Это сложность предметной области.
И сложности самой библиотеки тоже.
Z> WCF отлично ее скрывает, оставляя при этом множество механизмов тонкой настройки под себя.
Только механизмы эти, как только ты выходишь за рамки готовых сценарных комплектов настроек, ой как не просты. Конвеер behaviors неизмеримо сложнее (гибче потому что), нежели, скажем, sink stack в ремоутинге, хотя и он — не самая простая вещь.
Z> Что говорит об отличном дизайне.
Дизайн там, безусловно, неплохой. Только он ох как непрост.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
Z>Предлагаю закрыть спор об очевидности, как очевидно бесплодный.
Не я его начал.
Z>Если они в другом классе — они никак могут быть заменой методам с ограниченным доступом к собственным мемберам.
Речь идет о публичных членах класса.
Z>Почему вдруг? Никаких проблем не вижу. Хочу пример, как вынос метода (только вынос, без других телодвижений) помог что-то оттестировать.
// возвращаемся к нашему примеру.
// Foo() - метод который пользуется только публичным контрактом A, например A.Bar().
// В этом случае протестировать реализацию Foo() можно только вместе с реализацией Bar()
//public class A : IA
{
public int Bar()
{
}
public int Foo()
{
// use A.Bar();
}
}
// А если вынести Foo() в отдельный класс, то вместо A с совершенно конкретной
// реализацией Bar() можно подсунуть mock-объкет
//public static class IAHelper
{
public int static Foo(IA a)
{
// use a.Bar();
}
}
Здравствуйте, IB, Вы писали:
IB>Вот именно, а ты прицепился что статик хелперы плохи. Не нравятся хелперы, выноси в DI, локатор или еще какой сервис — не принципиально.
Только надо понимать, что DI имеет смысл, только если нужно оторвать зависимость от реализации. Если же реализация ровно одна (как в том же случае с XxxSubstring) — смысла в DI никакого.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
Z>>Спор сейчас о другом: стоит ли выносить в хелперы или DI сервисы все методы которые туда вынести возможно. IB>Вот именно, а ты прицепился что статик хелперы плохи. Не нравятся хелперы, выноси в DI, локатор или еще какой сервис — не принципиально.
Я прицепился именно к выделенному.
Z>> Я не согласен с тем, что во всех местах где это позволяет производительность и инкапсуляция метод должен быть вынесен. IB>Ты утверждаешь, что он должен быть оставлен, если так его "удобнее" использовать? Но рано или поздно все равно появится функционал, который из соображений "удобства" надо вкрячить в класс, а делать это уже будет поздно. И за что боролись?
Вероятность появления такого функционала определяется каждым конкретным случаем. А вероятность увеличить количество сущностей в системе при выносе всего подряд в сервисы или хелперы 100%. Увеличение количества сущностей несомненное зло, которое должно быть скомпенсировано реальной выгодой в каждом конкретном случае. Только не надо утрировать обратными примерами типа всех методов в одном файле, т.к. бездумное уменьшение количества сущностей добром не является.
IB>
IB>// возвращаемся к нашему примеру.
IB>// Foo() - метод который пользуется только публичным контрактом A, например A.Bar().
IB>// А если вынести Foo() в отдельный класс, то вместо A с совершенно конкретной
IB>// реализацией Bar() можно подсунуть mock-объкет
IB>
Стоп. Здесь кроме выноса метода из класса мы меняем еще и внешнюю к классу сущность, интерфейс.
На основании только одной конкретной реализации метода IA.Foo() в конкретном классе A.
Очевидно, что если у всех реализаций IA планируется одинаковый метод Foo(), его нужно вынести хотя бы из соображений дублирования кода. Независимо от изменения тестабилити метода. Такой вынос не относится к дискуссии.
Здравствуйте, Ziaw, Вы писали:
Z>Я прицепился именно к выделенному.
Где там преобладание статик хелперов?
Не верь глазам своим?
Z>Вероятность появления такого функционала определяется каждым конкретным случаем.
Если библиотекой действительно пользуются, то вероятность появления функционала, который в требуемом классе не реализован равна тем же самым 100%. За примерами далеко ходить не надо, берем тот же String.IsNullOrEmpty(), который появился далеко не сразу, а уж казалось бы все что можно запихнули.
Z>А вероятность увеличить количество сущностей в системе при выносе всего подряд в сервисы или хелперы 100%. Увеличение количества сущностей несомненное зло,
Очень сомнительное зло, откровенно говоря, особенно учитывая вышеизложенное (сущности все равно придется увеличивать, но функционал уже окажется размазан) и тот факт, что полученные сущности сгруппированы по функциональному признаку, что, если верить википедии, является несомненным добром.
Z> которое должно быть скомпенсировано реальной выгодой в каждом конкретном случае.
Описание реальной выгоды я уже давал. Эта выгода присутствует, в разной степени, конечно, но во всех случаях, без исключения. Стоит эту выгоду приносить в жертву "удобству" или нет — это уже другой вопрос, но то что выгода присутствует — это факт.
Z>Стоп. Здесь кроме выноса метода из класса мы меняем еще и внешнюю к классу сущность, интерфейс.
Интерфейс может быть тот же самый. Более того, этот интерфейс может быть специально введен для тестирования (если мы совсем упертые TDD-шники) и пущей формализации контрактов.
Z>Очевидно, что если у всех реализаций IA планируется одинаковый метод Foo(), его нужно вынести хотя бы из соображений дублирования кода.
Метода Foo() может не быть в интерфейсе IA. В конце-концов и никакого IA может не быть, но Bar() являться виртуальным — тогда Mock можно сделать просто наследником A.
IB>Не верь глазам своим?
А я ничего не имел против умеренного и разумного использования DI. Я против того, что любая возможность вынести метод объявляется достаточным основанием для создания сервиса, хелпера или стратегии.
Z>>Вероятность появления такого функционала определяется каждым конкретным случаем. IB>Если библиотекой действительно пользуются, то вероятность появления функционала, который в требуемом классе не реализован равна тем же самым 100%. За примерами далеко ходить не надо, берем тот же String.IsNullOrEmpty(), который появился далеко не сразу, а уж казалось бы все что можно запихнули.
Опять неудачный пример. Этот метод в принципе не мог быть экземплярным.
Z>>А вероятность увеличить количество сущностей в системе при выносе всего подряд в сервисы или хелперы 100%. Увеличение количества сущностей несомненное зло, IB>Очень сомнительное зло, откровенно говоря, особенно учитывая вышеизложенное (сущности все равно придется увеличивать, но функционал уже окажется размазан) и тот факт, что полученные сущности сгруппированы по функциональному признаку, что, если верить википедии, является несомненным добром.
Функционал точно также окажется размазан, если разнести его тонким слоем по функциональным группам на вырост.
Причем размазан 100%, а вот вырастет ли — не факт.
Z>> которое должно быть скомпенсировано реальной выгодой в каждом конкретном случае. IB>Описание реальной выгоды я уже давал. Эта выгода присутствует, в разной степени, конечно, но во всех случаях, без исключения. Стоит эту выгоду приносить в жертву "удобству" или нет — это уже другой вопрос, но то что выгода присутствует — это факт.
Вообще-то наоборот. Жертва удобству присутствует как факт, а вот выгоды мы можем увидеть вообще.
Z>>Стоп. Здесь кроме выноса метода из класса мы меняем еще и внешнюю к классу сущность, интерфейс. IB>Интерфейс может быть тот же самый. Более того, этот интерфейс может быть специально введен для тестирования (если мы совсем упертые TDD-шники) и пущей формализации контрактов. IB>В конце-концов и никакого IA может не быть, но Bar() являться виртуальным — тогда Mock можно сделать просто наследником A.
Моку наследнику А глубоко параллельно где находится метод Foo().
Впрочем существует вариант, когда Bar() является методом интерфейса, а Foo() нет. Тогда действительно в одном случае надо будет мокать наследником, а в другом достаточно интерфейсного мока, что проще. Ладно, вариант в котором вынос метода таки может облегчить тестирование найден. Хоть и достаточно экзотичный.
Z>> Я не согласен с тем, что во всех местах где это позволяет производительность и инкапсуляция метод должен быть вынесен. IB>Ты утверждаешь, что он должен быть оставлен, если так его "удобнее" использовать? Но рано или поздно все равно появится функционал, который из соображений "удобства" надо вкрячить в класс, а делать это уже будет поздно. И за что боролись?
Никогда ничего не поздно исправить. А если еще и писать код в духе: "если появиться вот такое требование -- архитектуру придется изменить (читай усложнить) в сторону {подставте нужное}, но сейчас это не надо, так что реализовываем наиболее простой/наиболее логичный вариант". Т.е. изначально готовить себя к изменениям, то они дадутся намного проще.
Z>>В первую очередь, вынос метода не должен снижать удобство работы с модулем. IB>В первую очередь дизайн модуля должен облегчать его поддержку и снижать вероятность ошибок при внесении изменений.
Да, только предсказать с большой долей правдоподобности что же именно понадобится изменять "через год" не сможет никто. Поэтому придется раздувать архитектуру покрыв все возможные "проблемные при изменениях" места.
Поэтому (раз уж все стали четко высказывать свою позицию) я буду делать так:
Обычно, после 2-3 мин обдумывания можно точно сказать куда лучше положить такой метод. Основываясь на логике, удобстве использования, удобстве сопровождения, наконец.
Если все же непонятно куда его девать (а я уверен что таких случаев не так уже и много), то можно подключить интуицию и "метод наменьших проблем".
С интуицией все понятно, а вот метод выглядит так:
Елси наш класс содеержит небольшое количество методов, а хелпера для него еще нет, то можно добавдобавить к нему еще один метод.
Если количество методов ничинает зашкаливать (МакКоннел говорил о 7-8 методах, вроде), то нужно организовать ему хелпер. А заодно и пересмотреть остальные методы: скорее всего в хелпер перекочует большая часть из них.
Если у нас и класс с большим количеством методов и хелперы у него наличиствуют, то нужно хорошенько подумать "а как получилась у нас такая корявка?"
Здравствуйте, Ziaw, Вы писали:
Z>А я ничего не имел против умеренного и разумного использования DI.
Еще раз. В данном конкретном случае ты примотался именно к статик-хелперам, других возражений не было.
Z>Я против того, что любая возможность вынести метод объявляется достаточным основанием для создания сервиса, хелпера или стратегии.
А я за — дальше что?
Z>Опять неудачный пример. Этот метод в принципе не мог быть экземплярным.
Ну возьми Concat, если этот не нравится или Join.
Z>Функционал точно также окажется размазан, если разнести его тонким слоем по функциональным группам на вырост.
Функциональная группа образуется как только появляется хоть один метод, она уже есть, это состоявшийся факт, не важно сколько методов ее образуют — так что это не на вырост, это под конкретную ситуацию.
Тонкого слоя тоже никакого, групп будет ровно столько, сколько надо и ни на одну больше, тут уже работает неоднократно склоняемый здесь SRP.
Z>Причем размазан 100%, а вот вырастет ли — не факт.
Как раз на 100% не размазан, а вот если оставить, то на 100% будут свалены в кучу методы выполняющие разный функционал.
Z>Вообще-то наоборот.
Вообще-то нет.
Z> Жертва удобству присутствует как факт, а вот выгоды мы можем увидеть вообще.
Удобство — штука относительная, а простота поддержки — практически абсолютная, так как может быть выражена конкретными метриками и основана на совершенно конкретных принципах и практиках, направленных именно на простоту поддержки кода.
Здравствуйте, IB, Вы писали:
A>>Может и наличие инопланетян, призраков, ... бесспорный факт? Раз это никто не смог опровергнуть? IB>Нет, родной, если вести дискуссию в таком ключе, то вы вообще слили еще до ее начала.
Я не веду дисскусию в подобном ключе. Я хотел показать всю абсурдность процитированного заявления.
Здравствуйте, Aikin, Вы писали:
A>Никогда ничего не поздно исправить.
Это ты погорячился.
A> изначально готовить себя к изменениям, то они дадутся намного проще.
Вот я и рассказываю, как поступить так, чтобы потом изменения дались проще.
A>Да, только предсказать с большой долей правдоподобности что же именно понадобится изменять "через год" не сможет никто.
Что конкретно — нет, а тенденции предугадать не так и сложно. К примеру, ты строки выносишь в константы и ресурсы или тупо хардкодишь, в надежде, что менять не придется? Так и тут, тоже самое.
A>Обычно, после 2-3 мин обдумывания можно точно сказать куда лучше положить такой метод.
А еще через пол-часа, переложить его окончательно, а потом, через пару дней, переложить еще раз и убедить себя, что уж с нового места ты его точно не стронешь.
A>С интуицией все понятно, а вот метод выглядит так: A>Елси наш класс содеержит небольшое количество методов, а хелпера для него еще нет, то можно добавдобавить к нему еще один метод. A>Если количество методов ничинает зашкаливать (МакКоннел говорил о 7-8 методах, вроде), то нужно организовать ему хелпер. А заодно и пересмотреть остальные методы: скорее всего в хелпер перекочует большая часть из них. A>Если у нас и класс с большим количеством методов и хелперы у него наличиствуют, то нужно хорошенько подумать "а как получилась у нас такая корявка?"
Это метод разбиения одного большего бардака на много маленьких.
Здравствуйте, Aikin, Вы писали:
A>Я не веду дисскусию в подобном ключе.
Именно в таком ты ее и ведешь. Контраргументов нет и по делу сказать нечего, значит будем просто игнорировать и на этом основании за аргумент не считать. В демагогическом споре, конечно, удобная позиция, но совершенно не конструктивная.
A>Я хотел показать всю абсурдность процитированного заявления.
Пока ты показал, что по делу возразить тебе нечего.
Здравствуйте, Aikin, Вы писали:
A>Обычно, после 2-3 мин обдумывания можно точно сказать куда лучше положить такой метод. Основываясь на логике, удобстве использования, удобстве сопровождения, наконец. A>Если все же непонятно куда его девать (а я уверен что таких случаев не так уже и много), то можно подключить интуицию и "метод наменьших проблем". A>С интуицией все понятно, а вот метод выглядит так: A>Елси наш класс содеержит небольшое количество методов, а хелпера для него еще нет, то можно добавдобавить к нему еще один метод. A>Если количество методов ничинает зашкаливать (МакКоннел говорил о 7-8 методах, вроде), то нужно организовать ему хелпер. А заодно и пересмотреть остальные методы: скорее всего в хелпер перекочует большая часть из них. A>Если у нас и класс с большим количеством методов и хелперы у него наличиствуют, то нужно хорошенько подумать "а как получилась у нас такая корявка?"
Архитектура сама по себе — штука бесполезная. Единственный смысл ее существование — упрощение отладки и модификации кода. Поэтому ее бессмысленно делать потом, когда код уже модифицирован, это будет работать только на последующие модификации. Та же стратегия, что ты описал, ИМХО, по сути формулируется одной фразой "забиваем на архтектуру на уровне контрактов классов, пока совсем не припечет".
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
A>>Никогда ничего не поздно исправить. IB>Это ты погорячился.
Ок, за исключением одной вещи -- смерти человека.
A>>Да, только предсказать с большой долей правдоподобности что же именно понадобится изменять "через год" не сможет никто. IB>Что конкретно — нет, а тенденции предугадать не так и сложно. К примеру, ты строки выносишь в константы и ресурсы или тупо хардкодишь, в надежде, что менять не придется? Так и тут, тоже самое.
Да, есть масса общепринятых практик. Твое правило к которым очевидно не относится.
A>>Обычно, после 2-3 мин обдумывания можно точно сказать куда лучше положить такой метод. IB>А еще через пол-часа, переложить его окончательно, а потом, через пару дней, переложить еще раз и убедить себя, что уж с нового места ты его точно не стронешь.
Программист страдающий амнезией? Занятно.
A>>С интуицией все понятно, а вот метод выглядит так: A>>Елси наш класс содеержит небольшое количество методов, а хелпера для него еще нет, то можно добавдобавить к нему еще один метод. A>>Если количество методов ничинает зашкаливать (МакКоннел говорил о 7-8 методах, вроде), то нужно организовать ему хелпер. А заодно и пересмотреть остальные методы: скорее всего в хелпер перекочует большая часть из них. A>>Если у нас и класс с большим количеством методов и хелперы у него наличиствуют, то нужно хорошенько подумать "а как получилась у нас такая корявка?" IB>Это метод разбиения одного большего бардака на много маленьких.
Маленький бардак может оказаться вовсе не бардаком.
Здравствуйте, AndrewVK, Вы писали:
AVK>Архитектура сама по себе — штука бесполезная. Единственный смысл ее существование — упрощение отладки и модификации кода. Поэтому ее бессмысленно делать потом, когда код уже модифицирован, это будет работать только на последующие модификации. Та же стратегия, что ты описал, ИМХО, по сути формулируется одной фразой "забиваем на архтектуру на уровне контрактов классов, пока совсем не припечет".
Не надо обобщать архитектуру вообще на данный конкретный случай.
Моя стратегия формулируется так: "в данном конкретном случае нет 100% правильного решения, а с другой стороны этот вопрос не является принципиальным (если ошибешся -- будет куча головной боли, гемороя и переписывания кода). Поэтому тратить на него [вопрос] более 5 минут -- бесполезная трата времени. А самое главное: переход от одного варианта к другому стоит всего двух циклов разработки".
P.S. Про переход: А в случае если этим кодом пользуешься только ты, то и одного достаточно.
Здравствуйте, IB, Вы писали:
IB>Еще раз. В данном конкретном случае ты примотался именно к статик-хелперам, других возражений не было.
Другие возражения были. Примотался я к статик хелперам потому, что этот вариант выноса самый корявый. Что доказывает отсутствие нормальных библиотек широко использующих эти самые хелперы.
Z>>Я против того, что любая возможность вынести метод объявляется достаточным основанием для создания сервиса, хелпера или стратегии. IB>А я за — дальше что?
Это я уже понял. Дальше ничего, я сомневался что моя точка зрения понятна, потому уточнил.
Z>>Опять неудачный пример. Этот метод в принципе не мог быть экземплярным. IB>Ну возьми Concat, если этот не нравится или Join.
Concat() нельзя вынести наружу, он использует приватные методы. То, что он статик тоже вполне логично, экземплярный не смог бы реализовать "An Empty string is used in place of any null argument". Как Join может быть экземплярным я тоже не понимаю, он должен вызываться у разделителя чтоли?
Я не пойму, что эти два метода доказывают? Спор идет о методах которые могут быть с равным успехом реализованы как экземплярными так и внешними. Пока не увидел ни одного выбора в пользу внешнего в общеизвестных библеотеках.
Z>>Функционал точно также окажется размазан, если разнести его тонким слоем по функциональным группам на вырост. IB>Функциональная группа образуется как только появляется хоть один метод, она уже есть, это состоявшийся факт, не важно сколько методов ее образуют — так что это не на вырост, это под конкретную ситуацию. IB>Тонкого слоя тоже никакого, групп будет ровно столько, сколько надо и ни на одну больше, тут уже работает неоднократно склоняемый здесь SRP. Z>>Причем размазан 100%, а вот вырастет ли — не факт. IB>Как раз на 100% не размазан, а вот если оставить, то на 100% будут свалены в кучу методы выполняющие разный функционал.
Ок, давай разместим IsNullOrEmpty, Concat, Join во внешние классы. Для простоты допустим, что вопрос корректной их работы в отрыве от String не существует, остался только вопрос расположения. Как должны выглядеть эти классы/класс?
Z>> Жертва удобству присутствует как факт, а вот выгоды мы можем увидеть вообще. IB>Удобство — штука относительная, а простота поддержки — практически абсолютная, так как может быть выражена конкретными метриками и основана на совершенно конкретных принципах и практиках, направленных именно на простоту поддержки кода.
Да ничего подобного, читабельность кода не поддается автоматической оценке, не существует метрик ее измеряющих. Тем не менее она может стать самой серьезной проблемой при модификации/расширении.
То, что некоторые метрики можно выразить числом еще не означает, что они доминирующие. Их просто легко измерить.
Здравствуйте, IB, Вы писали:
IB>В том-то и дело, что для C++, для C# вы так и не смогли привести ни одного убедительного сценария.
Тебе уже сто раз сказали, удобство важнее твоих фантиков.
Давай поставим вопрос ребром, зачем ты вообще решил увеличивать инкапуляцию? Ради упрощения поддержки? Ладно, я согласен, поддержка упростилась. Зачем тебе упрощать поддержку? Да всё очень просто, чтобы разработка стала дешевле. То есть конечно можно считать разного рода рейтинги, проводить аналитику, строить весёлые разноцветные графики, но это не цель, это средство. Все эти механизмы это жалкая (именно так!) попытка найти зависимость между структурой кода и ценой проекта. Попытка жалкая, но механизмов лучше пока нет.
Всё что выше не просто очевиданя правда, это вообще не специфично для C#, .Net, императивных языков или ООП. Это верно для Си, это верно для Ерланга, это, просто напросто, верно вообще для разработки проограммного обеспечения. Для любого производства где правильной организацией труда пытаются удешевить конечный продукт.
Теперь о главном. Жадный алгоритм не работает. Совсем. Оптимизировав цену каждого модуля в отдельности ты не получишь в итоге самый дешёвый проект. Причина тому проста — разные модули повторно используются разное количество раз и в разном объёме: некоторые вообще не используются повторно, а некоторые десятки раз. Стоимость функционала в библиотеке — это конечная стоимость, конечная стоимость функционала вне библиотеки — сумма стоимостей во всех приложениях. Это очевидная истина. Потому и пишут библиотеки, чтобы удешевлять, чтобы создать дорогой код, цена которого рассется по всем использующим его проектам.
Есть менее очевидные моменты, касающиеся даже не кода, а интерфейса в обощённом смысле. Разные библиотеки покрывающий один и тот же функционал не идентичны при использовании. У них разный интерфейс, какой-то удобнее (и дешевле в применении), какой-то менее очевиден и удобен. Конечно можно удешевить библиотеку оставив MiddleString внутри класса String, и вынеся LeftSubstring и RightSubstring наружу, как нам наивно предлагает AndrewVK. Можно удешевить библиотеку и... удорожить проект, потому что каждая минута, когда программист попадает в ступор не находя нужного метода, каждая минута, когда программист вспоминает что метод был, но не помнит где, каждая минута, когда он роется в документации, каждая менитура рефакторинга с метода который вспомнили, на метод который оптимален, оплачивается. Оплачивается в вечнозелёных американских долларах, в деревянных рублях, в евро, в лари, в гривнах, в чём угодно, но только не в рейтинге инкапсуляции. Имеет значение всё, например, формат файла конфигурации. У многих порог вхождения в Spring большой, потому что конфиграция в XML. Это сложность обучения и использования, методами с узкой областью применения ты их не измеришь.
Конечно можно встать в позу и сказть что вокруг все дураки и рейтинги рулят, но цену разработки это не отменит.
Здравствуйте, Aikin, Вы писали:
A>Ок, за исключением одной вещи -- смерти человека.
Ну пока код для игрушечных приложений пишется — оно конечно да, исправить можно все. Аты представляешь какой кровью дается обратная совместимость с предыдущими версиями API своего же продукта? Я бы посмотрел, как ты будешь исправлять свой код, на котором кастомер уже пару решений построил и впарил третьему клиенту.
A> Да, есть масса общепринятых практик. Твое правило к которым очевидно не относится.
Очевидно относится, так как основано на тех же самых практиках.
A>Программист страдающий амнезией? Занятно.
Это не занятно, это жизнь.
A>Маленький бардак может оказаться вовсе не бардаком.
Угу, а несколькими бардаками.
Здравствуйте, Ziaw, Вы писали:
Z>Другие возражения были.
Это какие? Единственное что тебя не устроило — это отсутствие статик хелперов.
Z>Примотался я к статик хелперам потому, что этот вариант выноса самый корявый. Что доказывает отсутствие нормальных библиотек широко использующих эти самые хелперы.
Во-первых, хелперы не для библиотек, для приложения хелперы вполне приемлемый вариант. А во-вторых, в восьмой раз тебе говорю, не хелперами едиными.
Z> Пока не увидел ни одного выбора в пользу внешнего в общеизвестных библеотеках.
WCF, любой IoC контейнер.
Z>Как должны выглядеть эти классы/класс?
Как внешний класс или сервис.
Z>Да ничего подобного, читабельность кода не поддается автоматической оценке, не существует метрик ее измеряющих. Тем не менее она может стать самой серьезной проблемой при модификации/расширении.
Только вопрос — ухудшает ли читаемость предлагаемый подход остается открытым.
Z>То, что некоторые метрики можно выразить числом еще не означает, что они доминирующие.
Это не означает, что их можно игнорировать.
Здравствуйте, adontz, Вы писали:
A>Тебе уже сто раз сказали, удобство важнее твоих фантиков.
Раз возразить нечего, значит фантики? Я не сомневался..
Я уже сто раз на это ответил, что приносить надежность кода в пользу удобству — дурная затея, тем более, что вопрос удобства остается открытым.
A> Да всё очень просто, чтобы разработка стала дешевле.
Ты пропустил один важный шаг — чтобы ошибок было меньше и обнаруживались они раньше.
Ром, у тебя косяк в логике: A>Теперь о главном. Жадный алгоритм не работает. Совсем. Оптимизировав цену каждого модуля в отдельности ты не получишь в итоге самый дешёвый проект
То что ниже, свосем не подтверждает фразу выше.. A>Причина тому проста — разные модули повторно используются разное количество раз и в разном объёме: некоторые вообще не используются повторно, а некоторые десятки раз. Стоимость функционала в библиотеке — это конечная стоимость, конечная стоимость функционала вне библиотеки — сумма стоимостей во всех приложениях. Это очевидная истина. Потому и пишут библиотеки, чтобы удешевлять, чтобы создать дорогой код, цена которого рассется по всем использующим его проектам.
Ну и так же, в своей пламенной речи, ты опять ловким образом забыл, про цену ошибки в библиотеке, которая расползется по всему использующему ее коду.
A> Можно удешевить библиотеку и... удорожить проект, потому что каждая минута, когда программист попадает в ступор не находя нужного метода, каждая минута, когда программист вспоминает что метод был, но не помнит где, каждая минута, когда он роется в документации, каждая менитура рефакторинга с метода который вспомнили, на метод который оптимален, оплачивается. A>Оплачивается в вечнозелёных американских долларах, в деревянных рублях, в евро, в лари, в гривнах, в чём угодно, но только не в рейтинге инкапсуляции.
Рома, слезь с броневика..
Хочешь, я распишу тебе про ужасы появления монстрообразных классов, борьбу за выживание в лапшеобразном коде, погрязшем в сильной связности, беготню от кастомеров, котрым ты в очередной раз все сломал из-за кривого API и обреченность разработчика осознавшего, что надо бы впихнуть еще один метод в класс, а уже нельзя?.. Я тоже так могу, просто время жалко.. =)
Но главный твой косяк в другом. Я тебе сейчас все объясню. Понимаешь, непосредственно на кодирование, поиск методов и копание в доке конкретной библиотеки уходит 1%-2% времени, все остальные 98% толковый разработчик думает. Меня уже очень много лет совершенно не парит объем предстоящего кодирование и мифическое "удобство". Это все равно другой порядок малости по сравнению со временем, которое я потрачу на проектирование.
Так что, на какой-нибудь не окрепший разум эта пламенная речь наверное произведет впечатление, но на серьезную аудиторию рассчитывать не советую..
A>Имеет значение всё, например, формат файла конфигурации. У многих порог вхождения в Spring большой, потому что конфиграция в XML. Это сложность обучения и использования, методами с узкой областью применения ты их не измеришь.
Ром, ты с XML-ем что ли не справился? XML сложен в изучении? Похоже тебя занесло..
Здравствуйте, IB, Вы писали:
IB>Но главный твой косяк в другом. Я тебе сейчас все объясню. Понимаешь, непосредственно на кодирование, поиск методов и копание в доке конкретной библиотеки уходит 1%-2% времени, все остальные 98% толковый разработчик думает.
То есть из 8 рабочих часов разработчик печатает от 4 минут 48 секунд, до 9 минут 36 секунд, а остальное время думает. Весьма реалистично.
Здравствуйте, IB, Вы писали:
IB>Я уже сто раз на это ответил, что приносить надежность кода в пользу удобству — дурная затея
Ты так и не привёл каких-либо доказательств этого сомнительного тезиса. То что хелперы уменьшают количество багов — это миф. Незначительное повышение инкапуляции за которое ты воюешь может как-то влиять на рефакторинг, но не на изначальное качество.
С другой стороны, сделать нормальный интерфейс, усложнив внутренее устройство, и, как следствие, потратить на разработку, включая отладку, больше времени, может быть, в целом, выгоднее по времени и деньгам.
Здравствуйте, IB, Вы писали:
IB>Ну и так же, в своей пламенной речи, ты опять ловким образом забыл, про цену ошибки в библиотеке, которая расползется по всему использующему ее коду.
Что значит расползёться? Это что вирус? Библиотеки потому, в том числе, и выгоднее что любой баг правиться только один раз, в библиотеке и никуда не расползается. Ты может придумал способ писать без багов? А вечный двигаетль заодно не изобрёл?
IB>Рома, слезь с броневика..
Ты утверждаешь что эффект от применения хелперов настолько силён, что количество багов заметно уменьшиться. И кто отстаивает, мягко говоря, сомнительные идеи? И кто на броневике?
IB>Хочешь, я распишу тебе про ужасы появления монстрообразных классов, борьбу за выживание в лапшеобразном коде, погрязшем в сильной связности, беготню от кастомеров, котрым ты в очередной раз все сломал из-за кривого API и обреченность разработчика осознавшего, что надо бы впихнуть еще один метод в класс, а уже нельзя?..
Иван, ты так и не понял, что, в отличие от тебя, никто в крайности не впадает, не просит все методы какие можно засовывать в классы, не просит все методы какие можно доставать из классов, и уж точно не о кривом API идёт речь. Речь как раз о прямом API, причём прямом с точки зрения тех, кто его использует, а не с точки зрения тех, кто проектирует. Не надо наводить красоту там где её не видно, за счёт тех мест где видно. Ты если бы не только писал, но и читал, не отвечал бы так невпопад.
A>>Имеет значение всё, например, формат файла конфигурации. У многих порог вхождения в Spring большой, потому что конфиграция в XML. Это сложность обучения и использования, методами с узкой областью применения ты их не измеришь. IB>Ром, ты с XML-ем что ли не справился? XML сложен в изучении? Похоже тебя занесло..
Во-первых, я написал "У многих". С чего ты взял, что у меня? Во-вторых, никто не говорил, что XML или язык конфигурации Spring, построенный на основе XML, сложен. Просто сам метод конфигурирования библиотеки кому-то может быть не близок. Ты отвечаешь на уровне "Не смог прочесть текст на испанском, потому что не умеет пользоваться программой Notepad". Вобщем, как всегда, переврал.
Здравствуйте, AndrewVK, Вы писали:
A>> потратить на разработку, включая отладку, больше времени, может быть, в целом, выгоднее по времени и деньгам. AVK>Знакомо.
Видимо плохо, раз минусуешь. Посмотри фильм Турецкий Гамбит
Здравствуйте, AndrewVK, Вы писали:
AVK>Наоборот, слишком хорошо. Мне последнее время все в большем объеме приходится чужой код до ума доводить.
Это твоё личное негатичное впечатление на тему "я в говне копаюсь, пока остальные творят". Есть фирме выгоднее так делать проект, твое личное негативное впечатление ничего не решает и никого не волнует. Если ошибочка вышла и не выгоднее, факты в студию (можно и о другом проекте), а иначе пустая болтовня получается.
Здравствуйте, adontz, Вы писали:
A>Это твоё личное негатичное впечатление на тему "я в говне копаюсь, пока остальные творят".
Не сочиняй. Я не говорил, что я в говне копаюсь. Просто я, по служебной необходимости, очень хорошо знаком с большим количеством разных "архитектурных" подходов. И неоднократно видел, к чему приводят подобные подходы в условиях промышленной разработки.
Ивану, кстати, насколько я знаю, тоже приходится руководить командой разработчиков, и следить за качеством их кода.
A> Есть фирме выгоднее так делать проект, твое личное негативное впечатление ничего не решает и никого не волнует.
Моей фирме — не выгодно так делать проект. Причем ОЧЕНЬ невыгодно. А фирмы, которых не волнует качество кода и его поддержка — не волнуют уже меня, как пример правильной архитектуры (и как потенциальные работодатели).
A> Если ошибочка вышла и не выгоднее, факты в студию (можно и о другом проекте), а иначе пустая болтовня получается.
У меня есть отличнейший факт, демонстрирующий все результаты такого подхода — реструктуризатор в Янусе. Он уже обошелся дороже, чем если бы в свое время я написал его сам.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Не сочиняй. Я не говорил, что я в говне копаюсь. Просто я, по служебной необходимости, очень хорошо знаком с большим количеством разных "архитектурных" подходов. И неоднократно видел, к чему приводят подобные подходы в условиях промышленной разработки. AVK>Ивану, кстати, насколько я знаю, тоже приходится руководить командой разработчиков, и следить за качеством их кода.
Ну ты ведь не пиписьками меряться собрался, а то я пишу правила по которым проводиться code review А ещё у меня за спиной 3 библиотеки на экспорт с ежедневным фидбеком от очень разных людей разного уровня и, как следствие, реальное понимание чего хотят пользователи от библиотеки. Однако, чей-либо личный авторитет, не может быть подтверждением той или иной идеи. Истина остаётся истиной вне зависимости от количества и знаменитости стороников, так что давай заканчивать говорить о себе. Тут очень многие были если не ПМ и архитекторами, то хотя бы старшими разработчиками. В дискуссии достаточно хамства и грязных намёков, не хватало только драки стенки на стенку.
A>> Если ошибочка вышла и не выгоднее, факты в студию (можно и о другом проекте), а иначе пустая болтовня получается. AVK>У меня есть отличнейший факт, демонстрирующий все результаты такого подхода — реструктуризатор в Янусе. Он уже обошелся дороже, чем если бы в свое время я написал его сам.
Янус — не библиотека и как пример, положительный или отрицательный, не уместен.
Здравствуйте, AndrewVK, Вы писали:
AVK>Судя по словам автора — да.
Я так понимаю, единственная (ну или самая существенная с большим отрывом от других) проблема Реструктуризатора в том, что он написан без использования внешних манипуляров-хелперов, как следствие код менее инкапсулирован чем хотелось бы и это в итоге привело к большому количеству проблем. Потому что мы тут обсуждаем хелперы и если проблема Реструктуризатора не в недостаточном выносе кода в хелперы, то пример Реструктуризатора непонятно что подтверждал бы. Если у Реструктуризатора другие проблемы, то это очень плохо, но оффтопик. Ну порайней мере область дискуссии стала бы слишком широка.
Здравствуйте, adontz, Вы писали:
A> Библиотеки потому, в том числе, и выгоднее что любой баг правиться только один раз, в библиотеке и никуда не расползается.
Вся проблема в том, что он вносится один раз, а обнаруживается в очень многих местах.
Тезис же о том, что правится такой баг только один раз, так же сомнителен, на практике ошибка в библиотеке зачастую выливается в серию воркэраундов в приложениях, которые ее используют, так как править библиотеку дорого и не всегда возможно. Потом выходит новая версия библиотеки, с уже исправленым багом, и бедный разработчик ставится в раскоряку, так как уже потрачены драгоценные человеко-дни на обход бага, теперь, вроде бы, надо писать новый код без лишних приседаний, но и старый выкидывать нельзя, так как есть активные ветки кода, которые используют старую версию. И вот, наш герой стоит перед суровой неизбежностью поддерживать две версии работы с библиотекой — с багом и без бага... (Дальше идет плачь о выкинутых деньгах, потраченном времени, провалу по срокам проекта, а дома детки не кормлены). Но! Зато API у библиотеки, чудо как удобен..
A>Ты может придумал способ писать без багов?
Нет, я просто описываю давно известный способ минимизировать число ошибок.
A>Ты утверждаешь что эффект от применения хелперов настолько силён, что количество багов заметно уменьшиться.
Про хелперы я ничего не утверждаю. Тебе еще раз принцип напомнить?
A> И кто отстаивает, мягко говоря, сомнительные идеи?
Ты конечно. Например, идея бросить все на алтарь удобства просто восхитительный образец сомнительности.
A> И кто на броневике?
Не будем показывать пальцем, но твой монументальный профиль торчит на нем с момента твоего вступления в дискуссию.. =)
A>Иван, ты так и не понял, что, в отличие от тебя, никто в крайности не впадает,
А, так это была еще не крайность?
A> не просит все методы какие можно засовывать в классы, не просит все методы какие можно доставать из классов,
Ну да, просто размажем все ровным слоем, как левая пятка захочет, зато эстетика соблюдена..
A> и уж точно не о кривом API идёт речь.
Прости, но прямым твой подход назвать — язык не поворачивается.
A>Во-первых, я написал "У многих". С чего ты взял, что у меня?
Больше я ни у кого сложностей с XML-ем не припомню..
A> Просто сам метод конфигурирования библиотеки кому-то может быть не близок.
Я верю, что в спринге может быть проблема с осознанием самой идеи контейнера, но чтобы были проблемы с пониманием идеи конфигурации... Ром, ты увлекся.. )) Сам по себе конфиг там мутный и стремный, но идея понятна с первого взгляда.. App.config проблем не вызывает? =)
Здравствуйте, adontz, Вы писали:
A>Я так понимаю, единственная (ну или самая существенная с большим отрывом от других) проблема Реструктуризатора в том, что он написан без использования внешних манипуляров-хелперов
Не единственная, но это там тоже имеется в классах описания структуры. А главная проблема — что писалось "как удобнее", а не как по нормальному. К примеру — вместо нормальной модели драйверов нужно наследоваться от специального класса, в котором гора виртуальных методов.
A> Ну порайней мере область дискуссии стала бы слишком широка.
Ты сам начал философствовать:
С другой стороны, сделать нормальный интерфейс, усложнив внутренее устройство, и, как следствие, потратить на разработку, включая отладку, больше времени, может быть, в целом, выгоднее по времени и деньгам.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, adontz, Вы писали:
A>С другой стороны, сделать нормальный интерфейс, усложнив внутренее устройство, и, как следствие, потратить на разработку, включая отладку, больше времени, может быть, в целом, выгоднее по времени и деньгам.
"Пилите Шура, пилите..." =)))
Здравствуйте, IB, Вы писали:
IB>Вся проблема в том, что он вносится один раз, а обнаруживается в очень многих местах. IB>Тезис же о том, что правится такой баг только один раз, так же сомнителен, на практике ошибка в библиотеке зачастую выливается в серию воркэраундов в приложениях, которые ее используют, так как править библиотеку дорого и не всегда возможно.
Не надо всё лепить в одну кучу, проблемы архитуктуры и плохой саппорт (закрытость исходников).
IB>Потом выходит новая версия библиотеки, с уже исправленым багом, и бедный разработчик ставится в раскоряку, так как уже потрачены драгоценные человеко-дни на обход бага, теперь, вроде бы, надо писать новый код без лишних приседаний, но и старый выкидывать нельзя, так как есть активные ветки кода, которые используют старую версию. И вот, наш герой стоит перед суровой неизбежностью поддерживать две версии работы с библиотекой — с багом и без бага... (Дальше идет плачь о выкинутых деньгах, потраченном времени, провалу по срокам проекта, а дома детки не кормлены). Но! Зато API у библиотеки, чудо как удобен..
Весьма душещипательная история, но те платные библиотеки с которыми я работал чинились максимум за неделю. Конечно, при выборе библиотеки важен так же и саппорт, но это совсем не имеет отношения к обсуждаемому вопросу.
A>> И кто отстаивает, мягко говоря, сомнительные идеи? IB>Ты конечно. Например, идея бросить все на алтарь удобства просто восхитительный образец сомнительности.
Не всё, я не фанатик. Ровно столько, сколько надо.
A>> не просит все методы какие можно засовывать в классы, не просит все методы какие можно доставать из классов, IB>Ну да, просто размажем все ровным слоем, как левая пятка захочет, зато эстетика соблюдена..
Ну если пользователя библиотеки зовут Левая Пятка, то да, именно так как захочет мистер Левая Пятка.
IB>проблемы с пониманием идеи конфигурации...
Не проблемы. Просто непривычно, а следовательно неудобно. Ты же понимаешь как летит самолёт, но понимать как он летит и управлять им всё же разные вещи.
Здравствуйте, AndrewVK, Вы писали:
AVK>А главная проблема — что писалось "как удобнее", а не как по нормальному. К примеру — вместо нормальной модели драйверов нужно наследоваться от специального класса, в котором гора виртуальных методов.
Андрей, ты так и не понял? Как удобнее, нон е создателю, а пользователю. Как как удобнее автору реструктуризатора, а как удобнее тебе. Если тебе не удобно пользоваться (наследование от непонятного класса, ещё что-то), то интерфейс плохой, библиотека плохая. ТОЧКА.
AVK>
С другой стороны, сделать нормальный интерфейс, усложнив внутренее устройство, и, как следствие, потратить на разработку, включая отладку, больше времени, может быть, в целом, выгоднее по времени и деньгам.
OK, сузим. Если пользователю будет заметно удобнее пользоваться библиотекой когда метод внутри класса, если, когда метод внутри класса, объект программный будет в большей степени соответствовать объекту из предметной области и это сделает интерфейс библиотеки интуитивно понятным в большей степени, то чхать я хотел на увеличение инкапуляции. Я пишу библиотеку для пользователй и мне должно быть сложно за счёт того что им легко, по определению.
Здравствуйте, IB, Вы писали:
IB>WCF, любой IoC контейнер.
Не оставалось никаких сомнений, что фургоны Вудли Пойндекстера шли по следам своих же колес.
— Наши следы! — пробормотал Колхаун; сделав это открытие, он натянул поводья и разразился проклятиями.
— Наши следы? Что ты этим хочешь сказать, Кассий? Неужели мы едем...
— ...по нашим собственным следам. Да, именно это я и хочу сказать. Мы описали полный круг. Смотрите: вот заднее копыто моей лошади — отпечаток половины подковы, а вот следы негров.
(с)
Ладно, контрпример: Spring.Net ISet, методы AddAll, RemoveAll. Единственные их имплементации используют только публичный контракт. Причем контракт интерфейса, как раз тот случай где еще удобство тестирования сыграет. Заодно и контракт ограничим.
По всем аргументам прозвучавшим в ветке отличные кандидаты для выноса в SetCollectionOperationService или еще куда, я не спец по таким выносам.
Косяк дизайна?
Z>>Как должны выглядеть эти классы/класс? IB>Как внешний класс или сервис.
Так сложно написать несколько строк контрактов? Может быть я спорю просто не разглядев очевидную логичность данного решения.
Z>>Да ничего подобного, читабельность кода не поддается автоматической оценке, не существует метрик ее измеряющих. Тем не менее она может стать самой серьезной проблемой при модификации/расширении. IB>Только вопрос — ухудшает ли читаемость предлагаемый подход остается открытым.
Вот контракты которые второй пост прошу увижу и посмотрим.
Z>>То, что некоторые метрики можно выразить числом еще не означает, что они доминирующие. IB>Это не означает, что их можно игнорировать.
Я где-то призывал их игнорировать? Я всеголишь считаю, что ориенитруясь только на задирание данных материк вверх многого не добьешься.
Здравствуйте, adontz, Вы писали:
A>Я так понимаю, единственная (ну или самая существенная с большим отрывом от других) проблема Реструктуризатора в том, что он написан без использования внешних манипуляров-хелперов,
Проблема в том, что он написан так, что куча методов реализованы там, где их "удобно использовать".
A> как следствие код менее инкапсулирован чем хотелось бы и это в итоге привело к большому количеству проблем.
Именно. Это привело к полной невозможности как модифицировать его код, так и использовать. Любая правка приводит к вылету в самом неожиданном месте, очень познавательный эффект..
A> Потому что мы тут обсуждаем хелперы
Хелперы обсуждаешь ты. Дискуссия же, напоминаю, ведется о том, где держать код метода пользующийся публичным контрактом класса — в классе или вне его.
Здравствуйте, adontz, Вы писали:
A>Андрей, ты так и не понял? Как удобнее, нон е создателю, а пользователю.
Это ты не понял — я говорил про пользователей, про тех, кто драйвера к замечательному реструктуризатору должен писать. Ведь отнаследоваться от одного класса и перекрыть несколько методов проще, чем разбираться с моделью драйверов и специализированными сервисами их обеспечения.
A>OK, сузим. Если пользователю будет заметно удобнее пользоваться библиотекой когда метод внутри класса, если, когда метод внутри класса, объект программный будет в большей степени соответствовать объекту из предметной области и это сделает интерфейс библиотеки интуитивно понятным в большей степени, то чхать я хотел на увеличение инкапуляции. Я пишу библиотеку для пользователй и мне должно быть сложно за счёт того что им легко, по определению.
Ну так чем меньше и специализированнее публичные контракты базовых сущостей библиотеки, тем проще поддерживать изменения кода, который ее использует.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
A>> Потому что мы тут обсуждаем хелперы IB>Хелперы обсуждаешь ты. Дискуссия же, напоминаю, ведется о том, где держать код метода пользующийся публичным контрактом класса — в классе или вне его.
Если это внешний интерфейс библиотеки, в классе. Если внутренности библиотеки или приложение, можно и в хелперах, если от этого есть польза. Плюс надо учитывать недостатки (убогость) статических методов, включая extension methods.
Здравствуйте, Ziaw, Вы писали:
Z>Косяк дизайна?
Весьма вероятно, я не краевед по Spring.Net
Z>Так сложно написать несколько строк контрактов?
Что это тебе даст? Ты можешь написать их сам, если очень хочется.
Z> Может быть я спорю просто не разглядев очевидную логичность данного решения.
Возможно.
Z>Я всеголишь считаю, что ориенитруясь только на задирание данных материк вверх многого не добьешься.
Я где-то писал, что ориентируюсь только на задирание данных метрик?
Здравствуйте, AndrewVK, Вы писали:
AVK>Это ты не понял — я говорил про пользователей, про тех, кто драйвера к замечательному реструктуризатору должен писать. Ведь отнаследоваться от одного класса и перекрыть несколько методов проще, чем разбираться с моделью драйверов и специализированными сервисами их обеспечения.
Ты меня запутал. В чём проблема с драйверами?
AVK>Ну так чем меньше и специализированнее публичные контракты базовых сущостей библиотеки, тем проще поддерживать изменения кода, который ее использует.
Да, только базовые сущности библиотеки должны как-то соответсвовать предметной области работы библиотеки. Если тебе удобнее разделить Солнце на СолнцеСвет и СолнцеТепло и это торчит наружу, то, надо заметить, нигде кроме библиотеки Солнце разделено не будет и у пользователя, после каждого взгляда на небо, будет чуть более съехавшая крыша и чуть более богатый словарный запас ненормативной лексики.
Здравствуйте, IB, Вы писали:
IB>"Пилите Шура, пилите..." =)))
Если вы огласитесь на мой проект, то спускаться из города на пристань вы будете по мраморным лестницам! Васюки станут центром десяти губерний! Что вы раньше слышали о городе Земмеринге? Ничего! А теперь этот городишко богат и знаменит только потому, что там был организован международный турнир, Поэтому я говорю: в Васюках надо устроить международный шахматный турнир.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, IB, Вы писали:
IB>>В том-то и дело, что для C++, для C# вы так и не смогли привести ни одного убедительного сценария.
A>Тебе уже сто раз сказали, удобство важнее твоих фантиков.
A>Давай поставим вопрос ребром, зачем ты вообще решил увеличивать инкапуляцию? Ради упрощения поддержки?
Для упрощения дальнейших модификаций, поскольку продукту нужна не только поддержка, но и дальнейшее развитие. Не эволюционирующий софт — мертвый софт(с). И критерием тут служит не сколько удобство модификаций, сколько их количество. Т.е. результативнее снизить количество необходимых изменений, а не улучшать их "удобство". Как минимум, уменьшается объем потенциально возможных ошибок, набор тестов, да и просто количество необходимого времени.
Принципы подобные SRP, которые применяют для управления зависимостями, как раз и предназначены для минимизации области изменения взаимодействующих объектов. Ответственность, в терминах SRP, это просто причина возможных изменений, и один класс должен иметь только одну причину для изменения.
A>Ладно, я согласен, поддержка упростилась. Зачем тебе упрощать поддержку? Да всё очень просто, чтобы разработка стала дешевле.
Дело не только в разработке как таковой: Проект живет, заказчики требуют новых возможнотей. Соответственно требуются изменения в коде. А вот снижение количества изменений, снижение количества потенциально возможных багов порожденных этими изменениями, снижение объемов тестирования, уменьшение времени, затраченного на эти изменения, увеличиние скорости реализации новых фич и т.п., — все это тоже в конечном итоге средства снижения стоимости и увеличения конкурентноспособности. "Обратная" сторона медали — адекватная архитектура, способствующая к этому... А SRP — это просто один из принципов ее достижения.
A>Теперь о главном. Жадный алгоритм не работает. Совсем. Оптимизировав цену каждого модуля в отдельности ты не получишь в итоге самый дешёвый проект. Причина тому проста — разные модули повторно используются разное количество раз и в разном объёме: некоторые вообще не используются повторно, а некоторые десятки раз.
Повторному использованию, имхо, очень часто мешают лишние ответственности.
>Стоимость функционала в библиотеке — это конечная стоимость, конечная стоимость функционала вне библиотеки — сумма стоимостей во всех приложениях. Это очевидная истина. Потому и пишут библиотеки, чтобы удешевлять, чтобы создать дорогой код, цена которого рассется по всем использующим его проектам.
А такой код(библиотечный) тем более должен стремится к применению SRP по максимуму. Если я хочу от библиотеки 'A', то она и должна мне предоставить 'A', а не ['A' + 'B' + на всякий случай 'C' для удобства] в одном флаконе. Это просто снижает повторное использование(вместе с окупаемостью).
A>Конечно можно удешевить библиотеку оставив MiddleString внутри класса String, и вынеся LeftSubstring и RightSubstring наружу, как нам наивно предлагает AndrewVK.
Можно пойти еще дальше и оставить только два итератора, плюс сделать строку иммутабельной и добавить string builder.
A>Можно удешевить библиотеку и... удорожить проект, потому что каждая минута, когда программист попадает в ступор не находя нужного метода, каждая минута, когда программист вспоминает что метод был, но не помнит где, каждая минута, когда он роется в документации, каждая менитура рефакторинга с метода который вспомнили, на метод который оптимален, оплачивается....
Т.е. автокомплит, если я правильно понял всю ветку — это и есть удобство ?
Здравствуйте, AndrewVK, Вы писали:
AVK>Если их использовать по делу
Золотые слова.
AVK>никаких таких фатальных недостатков у них нет.
Я знаю, как минимум, два.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Console35
{
class Test
{
}
static class TestExtender
{
public static/* virtual ага, щас, разбежался */void ExtensionMethod(this Test test)
{
}
}
class Program
{
static void Main(string[] args)
{
Test t = null;
t.ExtensionMethod(); // Опа, для кого мы это вызвали?!
}
}
}
Здравствуйте, adontz, Вы писали:
AVK>>Это ты не понял — я говорил про пользователей, про тех, кто драйвера к замечательному реструктуризатору должен писать. Ведь отнаследоваться от одного класса и перекрыть несколько методов проще, чем разбираться с моделью драйверов и специализированными сервисами их обеспечения.
A>Ты меня запутал. В чём проблема с драйверами?
Ну как же. Текущий вариант для создания драйвера — это огромный класс, от которого наследуешься и перекрываешь нужные тебе мметоды. Причем перекрыть можно почти любой аспект работы реструктуризатора, потому что этот базовый класс по сути реструктуризатор в себе и содержит. И кроме этого класса не надо больше никаких других.
Тем не менее, если хоть немного включить голову, то понятно, что алгоритмы сравнения схем нужно вынести в отдельный класс, занимающийся сравнением, код, который непосредственно модифицирует базу — в отдельный класс, куча методов сравнения элементов схемы из классов схемы в хелпер, код загрузки схем из БД и ресурсов — тоже должны быть отдельными классами и т.п.
A>Да, только базовые сущности библиотеки должны как-то соответсвовать предметной области работы библиотеки.
И при чем тут распределение методов по классам?
A> Если тебе удобнее разделить Солнце на СолнцеСвет и СолнцеТепло и это торчит наружу, то, надо заметить, нигде кроме библиотеки Солнце разделено не будет и у пользователя
Это всегда будет так — пользователь всегда будет добавлять свои, специфичные алгоритмы, и добавлять он их скорее всего будет не при помощи наследования от классов библиотеки.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, adontz, Вы писали:
A>Не надо всё лепить в одну кучу, проблемы архитуктуры и плохой саппорт (закрытость исходников).
Рома, не увиливай, ни плохой саппорт, ни за крытость исходников тут не причем..
A>Весьма душещипательная история,
Надеюсь не менее душещипательная чем твоя.
A>Не всё, я не фанатик.
Меня терзают смутные сомненья. "Ну если пользователя библиотеки зовут Левая Пятка, то да, именно так как захочет мистер Левая Пятка." (с)
A>Ровно столько, сколько надо.
О, как ты быстро назад сдаешь.. ) А сколько надо? На глазок решишь?
A>Не проблемы. Просто непривычно, а следовательно неудобно.
App.config — не очень неудобен? Не жмет?
Что может быть непривычного в конфигурационных файлах? )
Да и проблема с удобством решилась — привыкли и сразу стало удобно, этот аргумент, значит, тоже вычеркиваем..
Здравствуйте, AndrewVK, Вы писали:
AVK>Это всегда будет так — пользователь всегда будет добавлять свои, специфичные алгоритмы, и добавлять он их скорее всего будет не при помощи наследования от классов библиотеки.
Пользователь пускай, зачем это делать автору библиотеки? Кому нужна такая бяка?
Здравствуйте, adontz, Вы писали:
AVK>>Это всегда будет так — пользователь всегда будет добавлять свои, специфичные алгоритмы, и добавлять он их скорее всего будет не при помощи наследования от классов библиотеки.
A>Пользователь пускай, зачем это делать автору библиотеки?
А это уже не важно — как справедливо заметил Мейерс, это уже есть де факто, и бесполезно с этим бороться, тем более путем утяжеления публичных интерфейсов.
A> Кому нужна такая бяка?
Мне.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, adontz, Вы писали:
A>Если это внешний интерфейс библиотеки, в классе.
Всю библиотеку в одном классе? Смело.. )
A>Борьба идёт за внешний интерфейс библиотеки.
"Этот стон у нас песней зовется.." (с)
Здравствуйте, IB, Вы писали:
Z>>Косяк дизайна? IB>Весьма вероятно, я не краевед по Spring.Net
Для этого надо быть краеведом в спринге? Бог с ним, пусть я проектирую абстрактную библиотеку.
Если интерфейс ISet, несколько реализаций. Нужны методы AddAll, RemoveAll. Представляют из себя вызов Add/Remove в foreach по IEnumerable параметру. Куда мне их деть? Только пожалуйста без общих рекомендаций типа "куда угодно". Любое конкретное решение.
Z>>Так сложно написать несколько строк контрактов? IB>Что это тебе даст? Ты можешь написать их сам, если очень хочется.
Мне это даст понимание правильного с твоей точки зрения выноса. Если я напишу их сам никакого смысла в этом не будет.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
A>>Давай поставим вопрос ребром, зачем ты вообще решил увеличивать инкапуляцию? Ради упрощения поддержки? ЮЖ>Т.е. результативнее снизить количество необходимых изменений, а не улучшать их "удобство". Как минимум, уменьшается объем потенциально возможных ошибок, набор тестов, да и просто количество необходимого времени.
Верно, но не про нас. То где находится метод: в классе или хелпере, никак не влияет ни на возможность его тестировать, ни на возможность его поменять или добавить новый метод. Прсото взяли и для себя упорядочили. Подчёркиваю, для себя. Другим эта упорядоченность на фиг не упала.
ЮЖ>один класс должен иметь только одну причину для изменения.
Утверждение, которое можно трактовать как угодно.
ЮЖ>А вот снижение количества изменений, снижение количества потенциально возможных багов порожденных этими изменениями, снижение объемов тестирования, уменьшение времени, затраченного на эти изменения, увеличиние скорости реализации новых фич и т.п., — все это тоже в конечном итоге средства снижения стоимости и увеличения конкурентноспособности.
ОК, как вынос метода в хелпеер на это влияет? А? Не изменение архитектуры, а просто взяли и сделали второй класс и вытащили туда всё чему достаточно публичного контракта.
ЮЖ>Повторному использованию, имхо, очень часто мешают лишние ответственности.
Опять не в тему. Библиотека не стала делать больше или меньше, просто методы перераскидали по классам.
ЮЖ>А такой код(библиотечный) тем более должен стремится к применению SRP по максимуму. Если я хочу от библиотеки 'A', то она и должна мне предоставить 'A', а не ['A' + 'B' + на всякий случай 'C' для удобства] в одном флаконе. Это просто снижает повторное использование(вместе с окупаемостью).
Опять не в тему. Библиотека не стала делать больше или меньше, просто методы перераскидали по классам.
ЮЖ>Можно пойти еще дальше и оставить только два итератора, плюс сделать строку иммутабельной и добавить string builder.
И получить STL где у сттроки нет нормального find И каждый пишет свой. Не надо SRP доводить до маразма стимулирую написание велосипедов.
ЮЖ>Т.е. автокомплит, если я правильно понял всю ветку — это и есть удобство?
Конечно, автокомплит увеличивает производительность. Кто бы спорил.
Здравствуйте, IB, Вы писали:
A>>Ровно столько, сколько надо. IB>О, как ты быстро назад сдаешь.. ) А сколько надо? На глазок решишь?
А я не сдаю назад, я же говорю, тут только ты впадаешь в крайности. На глазок ли? Ну не наобум, но да, в каждом конкретном случае решение может приниматься индивидуально, на основе личного опыта. Жёстких правил нет, правила вссегоо лишь рекомендуют.
IB>Что может быть непривычного в конфигурационных файлах? )
Конфигурационные файлы определённого формата могут быть непривычны.
IB>Да и проблема с удобством решилась — привыкли и сразу стало удобно, этот аргумент, значит, тоже вычеркиваем..
Здравствуйте, AndrewVK, Вы писали:
A>>Я знаю, как минимум, два. AVK>Во, прекрасный пример использования не по делу.
Вот и я о том же, хелперы не панацея и нечего пихать в них всё что можно, прсото потому что можно.
Что это не по делу я знаю, я это как раз как пример использования "не по делу" привёл.
Здравствуйте, Ziaw, Вы писали:
Z> Любое конкретное решение.
Опять конкретные решения. Прошлый ничего не показал? Данное конкретное решение уже есть в фреймворке, если я правильно понял — это метод AddRange у класса List<T>. И оно должно быть внутри класса, потому что требует доступа к внутреннему массиву. Что же касается интерфейса IList<T>, то там такого метода нет. И если оно там понадобится — безусловно это будет статический хелпер, хотя бы потому что реализацию в интерфейс добавить не получится (в качестве примера метод Enumerable.Concat() из того же фреймворка).
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Данное конкретное решение уже есть в фреймворке
Еща раз. У меня стоит задача спроектировать библиотеку работы со множествами, есть требование обеспечить возможность добавлять в ISet множество IEnumerble<T> вызовом одного метода. Сам метод выглядит очень просто
public static AddAll<T>(ISet<T> set, IEnumerable<T> items)
{
foreach (T item in items)
set.Add(item);
}
Либо
public AddAll<T>(IEnumerable<T> items)
{
foreach (T item in items)
this.Add(item);
}
Какой мне предпочесть опираясь на OCP или другие соображения?
Здравствуйте, adontz, Вы писали:
A>Тем не менее безусловно пихали в хелперы всё что можно вытащить из основного класса.
Панацея, Рома, это согласно БСЭ:
1) у алхимиков лекарство, якобы исцеляющее от всех болезней, названное по имени древнегреческой богини Панакии (Panakeia — всеисцеляющая). 2) В переносном смысле (иронически) — средство, избавляющее от всех зол, для решения всех проблем.
Очевидно — от всех зол правило Мейерса не избавляет, всего лишь позволяет сделать еще один шажок на пути улучшения manageability исходного кода.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Панацея, Рома, это согласно БСЭ: AVK>
1) у алхимиков лекарство, якобы исцеляющее от всех болезней, названное по имени древнегреческой богини Панакии (Panakeia — всеисцеляющая). 2) В переносном смысле (иронически) — средство, избавляющее от всех зол, для решения всех проблем.
Спасибо за цитату, но зря. Она у меня есть бумажная, весь 31 том.
AVK>Очевидно — от всех зол правило Мейерса не избавляет, всего лишь позволяет сделать еще один шажок на пути улучшения manageability исходного кода.
ОК, зачем его использовать "на всякий случай", когда видимой выгоды нет?
Здравствуйте, adontz, Вы писали:
A>Спасибо за цитату, но зря. Она у меня есть бумажная, весь 31 том.
Я ж не тебе лично письмо пишу.
A>ОК, зачем его использовать "на всякий случай", когда видимой выгоды нет?
Что значит "на всякий случай" и "видимой выгоды нет"? Увеличение инкапсуляции и уменьшение связности — вполне конкретная выгода. А если у нас у класса есть еще публичный интерфейс — то и минимизация его размеров.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Увеличение инкапсуляции и уменьшение связности — вполне конкретная выгода.
Увеличение инкапсуляции и уменьшение связности это метод, а не результат.
AVK>А если у нас у класса есть еще публичный интерфейс — то и минимизация его размеров.
Самообман. Интерфейс не стал меньше, просто в нём стало больше классов.
Здравствуйте, adontz, Вы писали:
A>Увеличение инкапсуляции и уменьшение связности это метод, а не результат.
Конечный результат — увеличение manageability кода. Мы пошли по кругу.
AVK>>А если у нас у класса есть еще публичный интерфейс — то и минимизация его размеров.
A>Самообман. Интерфейс не стал меньше, просто в нём стало больше классов.
Два маленьких интерфейса — намного лучше одного большого.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Отлично, я быстренько все это пишу. Отдаю библиотеку все используют — все в восторге.
Однако через год обнаруживается, что в многопоточных приложениях ее использовать нельзя, т.к. отсутствует потокобезопасный тип множества.
И меня спрашивают,
— Использовал ли ты OPC в своей библиотеке?
— Ага (конечно же я помню ту замечательную дискуссию на рсдн где мы наконец пришли к консенсусу по поводу выноса методов).
— Ну так добавь быстренько SynchronizedSet
— Яволь.
Я сажусь за код и с ужасом понимаю, что мне приходится в методах AddAll/RemoveAll писать замечательные конструкции типа:
Ужас он не от того, что мне пришлось это сделать, а от того, что я понимаю, что еще пара вариантов реализации AddAll и мне придется признаться, что мой дизайн унылое г.
Здравствуйте, Ziaw, Вы писали:
Z>public static AddAll<T>(this ISet<T> set, IEnumerable<T> items) Z>{ Z> if (set is ISynchronizedSet)
Да уж. А сделать:
public static AddAll<T>(this ISynchronizedSet<T> set, IEnumerable<T> items)
Религия мешает?
Z>Ужас он не от того, что мне пришлось это сделать, а от того, что я понимаю, что еще пара вариантов реализации AddAll и мне придется признаться, что мой дизайн унылое г.
Неа, унылое гавно, это когда в уже существующих методах вдруг появляется блокировка. Ну а главное — тема сис... вынесения методов не раскрыта. Где здесь преимущество варианта, когда твои AddAll находятся в ISet?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
AVK>Религия мешает?
Ну вообще-то я не планировал открывать наружу ISynchronizedSet, а использовал его как internal проводник private поля.
Хотя смысл открыть наружу его есть, только тогда придется вылавливать ошибки там, где люди написали:
ISet s = new SynchronizedSet();
s.AddAll(collection);
В принципе для таких случаев можно придумать экзамен, пусть 100 раз на доске напишут.
Если мне нужна синхронизация при вызове AddAll я всегда буду строго указывать тип ISynchronizedSet.
Z>>Ужас он не от того, что мне пришлось это сделать, а от того, что я понимаю, что еще пара вариантов реализации AddAll и мне придется признаться, что мой дизайн унылое г.
AVK>Неа, унылое гавно, это когда в уже существующих методах вдруг появляется блокировка. Ну а главное — тема сис... вынесения методов не раскрыта. Где здесь преимущество варианта, когда твои AddAll находятся в ISet?
Меня тут просто замучали тыкать в факт того, что появиться может что угодно и где угодно!
При этом упирая на то, что вынос методов служит как раз облегчения расширения в таких ситуациях.
Как только этот факт перестал работать вашу пользу — его сразу списали в канализацию.
Здравствуйте, AndrewVK, Вы писали:
AVK>Ну а главное — тема сис... вынесения методов не раскрыта. Где здесь преимущество варианта, когда твои AddAll находятся в ISet?
Тут как раз просто, я его могу переопределить в классе SynchronizedSet.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Юрий Жмеренецкий, Вы писали:
A>>>Давай поставим вопрос ребром, зачем ты вообще решил увеличивать инкапуляцию? Ради упрощения поддержки? ЮЖ>>Т.е. результативнее снизить количество необходимых изменений, а не улучшать их "удобство". Как минимум, уменьшается объем потенциально возможных ошибок, набор тестов, да и просто количество необходимого времени.
A>Верно, но не про нас. То где находится метод: в классе или хелпере, никак не влияет ни на возможность его тестировать, ни на возможность его поменять или добавить новый метод. Прсото взяли и для себя упорядочили. Подчёркиваю, для себя. Другим эта упорядоченность на фиг не упала.
Еще как упала. Раз уж была упомянута STL: хороший пример — разделение алгоритмов и контейнеров: все что необходимо, так это предоставить пару итераторов — все остальное начнет работать автоматически. То же и для алгоритмов поверх любых контейнеров. А что будет если слить все в кучу ?
ЮЖ>>один класс должен иметь только одну причину для изменения. A>Утверждение, которое можно трактовать как угодно.
Отнюдь, трактовка ровно одна: один класс — одна причина для изменений.
Вот AndrewVK рядом(не знаю как ссылки на конкретные сообщения из плоского веб-интерфейса вставлять) про реструктуризатор писал:
Тем не менее, если хоть немного включить голову, то понятно, что алгоритмы сравнения схем нужно вынести в отдельный класс, занимающийся сравнением, код, который непосредственно модифицирует базу — в отдельный класс, куча методов сравнения элементов схемы из классов схемы в хелпер, код загрузки схем из БД и ресурсов — тоже должны быть отдельными классами и т.п.
Здесь множество причин для изменений. Я про это.
ЮЖ>>А вот снижение количества изменений, снижение количества потенциально возможных багов порожденных этими изменениями, снижение объемов тестирования, уменьшение времени, затраченного на эти изменения, увеличиние скорости реализации новых фич и т.п., — все это тоже в конечном итоге средства снижения стоимости и увеличения конкурентноспособности.
A>ОК, как вынос метода в хелпеер на это влияет? А? Не изменение архитектуры, а просто взяли и сделали второй класс и вытащили туда всё чему достаточно публичного контракта.
При необходимости изменения кода находящегося с хелпере — будет изменен только код в нем, место внесения изменений локализовано, и что самое главное: это изменение не затрагивает сам класс.
ЮЖ>>Повторному использованию, имхо, очень часто мешают лишние ответственности. A>Опять не в тему. Библиотека не стала делать больше или меньше, просто методы перераскидали по классам.
Тогда зачем вообще раскидывать? — один класс на библиотеку, да и не мучиться...
ЮЖ>>А такой код(библиотечный) тем более должен стремится к применению SRP по максимуму. Если я хочу от библиотеки 'A', то она и должна мне предоставить 'A', а не ['A' + 'B' + на всякий случай 'C' для удобства] в одном флаконе. Это просто снижает повторное использование(вместе с окупаемостью).
A>Опять не в тему. Библиотека не стала делать больше или меньше, просто методы перераскидали по классам.
Из нескольких классов, которые можно использовать независимо, проще собирать то что необходимо.
ЮЖ>>Можно пойти еще дальше и оставить только два итератора, плюс сделать строку иммутабельной и добавить string builder. A>И получить STL где у сттроки нет нормального find
А нормальный это какой ? с регэкспами и поиском через гугл?
A>И каждый пишет свой.
Может быть. В зависимости от задачи. Со строками проблема в том, что там слишком много потенциально настраиваемых моментов, и при попытке свалить все в кучу получится вышеупомянутый реструктуризатор.
A>Не надо SRP доводить до маразма стимулирую написание велосипедов.
Не надо доводить до маразма кажущееся "удобство" в виде автокомплита. Иначе чем объяснить изначальное существование в классе методов которых там не должно быть?
ЮЖ>>Т.е. автокомплит, если я правильно понял всю ветку — это и есть удобство? A>Конечно, автокомплит увеличивает производительность. Кто бы спорил.
Увеличивает производительность чего ? навигации по толстым классам ? Сначала сделали себе проблему, а потом ее же и решаем =)
Ничего против автокомплита не имею, но неужели это действительно может рассматриваться как критерий выбора библиотеки ?
Здравствуйте, Ziaw, Вы писали:
Z>Тут как раз просто, я его могу переопределить в классе SynchronizedSet.
И чем это лучше AddAllSync? А что, если для одной и той же коллекции нужен и с блокировкой доступ и без онной (как минимум в конструкторе с сигнатурой (IEnumerable<T>) блокировка не нужна)? Вводить специальное свойство еще — IsLockActive? Как думаешь, почему в ICollection есть свойства IsSynchronized и SyncRoot, а в ICollection<T> уже ничего подобного нет? Почему близкий по семантике твоему примеру метод Union находится в хелпере, а не в интерфейсе IEnumerable<T>? Это все ошибки дизайна? Какие из методов класса Enumerable по твоему должны находиться в интефейсе IEnumerable<T> и почему? А какие не должны и почему?
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Ziaw, Вы писали:
Z>>Ну вообще-то я не планировал открывать наружу ISynchronizedSet AVK>Ну начинается.
Что начинается? Не открою плохо, открою еще хуже.
Z>>Меня тут просто замучали тыкать в факт того, что появиться может что угодно и где угодно! AVK>Увы, но это действительно непреложый факт. Z>>При этом упирая на то, что вынос методов служит как раз облегчения расширения в таких ситуациях. AVK>Ага.
Так вот вполне реалистичная ситуация когда появившееся требование ложится на дизайн с вынесенным методом гораздо хуже чем на экземплярный.
Z>>Как только этот факт перестал работать вашу пользу — его сразу списали в канализацию. AVK>Кого списали в канализацию? Я лично никого в канализацию не списывал.
А как же это? AVK>Неа, унылое гавно, это когда в уже существующих методах вдруг появляется блокировка
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Ziaw, Вы писали:
Z>>Тут как раз просто, я его могу переопределить в классе SynchronizedSet. AVK>И чем это лучше AddAllSync?
Лучше тем, что передав экземпляр SynchronizedSet активно исползующийся в другом треде в любой метод с принимающий ISet я не буду гадать, будет там вызван AddAll или нет.
AVK>А что, если для одной и той же коллекции нужен и с блокировкой доступ и без онной (как минимум в конструкторе с сигнатурой (IEnumerable<T>) блокировка не нужна)? Вводить специальное свойство еще — IsLockActive?
В чьем конструкторе?
AVK>Как думаешь, почему в ICollection есть свойства IsSynchronized и SyncRoot, а в ICollection<T> уже ничего подобного нет?
Почему? Мне правда интересно.
AVK>Почему близкий по семантике твоему примеру метод Union находится в хелпере, а не в интерфейсе IEnumerable<T>?
Как минимум по причине обратной совместимости.
AVK>Это все ошибки дизайна? Какие из методов класса Enumerable по твоему должны находиться в интефейсе IEnumerable<T> и почему? А какие не должны и почему?
Я не выдумывал правил по выносу, я только показал, что правило выдуманное IB для улучшения расширяемости может сработать с точностью до наоборот.
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, AndrewVK, Вы писали:
AVK>>Здравствуйте, Ziaw, Вы писали:
Z>>>Какой мне предпочесть опираясь на OCP или другие соображения?
AVK>>
Z>Отлично, я быстренько все это пишу. Отдаю библиотеку все используют — все в восторге. Z>Однако через год обнаруживается, что в многопоточных приложениях ее использовать нельзя, т.к. отсутствует потокобезопасный тип множества.
Обычно на это сразу внимание обращают.
Кстати, раз вопрос коснусля блокировок: внутренняя блокировка в контейнере — вещь практически бесполезная. Блокировка контейнеров должна быть внешней. Вот скажем мне нужно за одну блокировку вызвать три раза AddAll для разных источников...
Здравствуйте, Ziaw, Вы писали:
Z>>>Ну вообще-то я не планировал открывать наружу ISynchronizedSet AVK>>Ну начинается. Z>Что начинается?
Начинается изменение задачи на ходу. Т.е. требовать в примере с Substring абсолютной конкретики можно, а тут вдруг задача с каждым сообщением меняется произвольным образом. Если хочешь конкретики — то твое решение никуда не годится, прозрачное и закрытое введение блокировки до добра не доводит. Завтра тебе понадобится несколько последовательных добавлений и удалений — будем вводить в базовый интерфейс метод AddAndRemoveAll? А знаешь в чем самое веселое — вариант с хелперами легко и непринужденно превращается в вариант с методами класса, причем, благодаря extension методам, еще и абсолютно прозрачно для прикладного кода, использующего ISet, на уровне исходников (т.е. код надо просто перекомпилировать). А вот если ты вкрячил AddAll в ISet, а в результате никаких синхронизаций не понадобилось, выкинуть его оттуда будет ой как непросто. И придется несчастным реализаторам копипейстить один и тот же код твоих AddAll. Так где здесь унылое гавно в дизайне?
Z>Так вот вполне реалистичная ситуация когда появившееся требование ложится на дизайн с вынесенным методом гораздо хуже чем на экземплярный.
Пока что я вижу обратное.
AVK>>Кого списали в канализацию? Я лично никого в канализацию не списывал. Z>А как же это? AVK>>Неа, унылое гавно, это когда в уже существующих методах вдруг появляется блокировка
То есть факт, который работал в нашу пользу — это когда в уже существующих методах вдруг появляется блокировка?
Слушай, мне совершенно не интересно доказывать, что я прав. Если тебе так будет приятно — я признаю что прав ты и больше не буду с тобой спорить.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
Z>>>Тут как раз просто, я его могу переопределить в классе SynchronizedSet. AVK>>И чем это лучше AddAllSync? Z>Лучше тем, что передав экземпляр SynchronizedSet активно исползующийся в другом треде в любой метод с принимающий ISet я не буду гадать, будет там вызван AddAll или нет.
Понимаешь, блокировка такая штука, что ее не стоит прятать внутри. Иначе такие суперрешения будут — мама не горюй, перерасход на блокировках и дедлоки. Только внешний код знает, как правильно расставлять локи, не надо это внутрь коллекций запихивать. Максимум что тут можно сделать — ввести специальную синхронизированную коллекцию с методами, находящимися в публичном интерфейсе класса, но не в ISet, либо отдельный хелпер, увязанный, опять же, не с ISet, а с конкретным синхронизированным классом. Именно так устроен CDS (Coordination Data Structures), в отличие от кривого решения в первом фреймворке.
AVK>>А что, если для одной и той же коллекции нужен и с блокировкой доступ и без онной (как минимум в конструкторе с сигнатурой (IEnumerable<T>) блокировка не нужна)? Вводить специальное свойство еще — IsLockActive? Z>В чьем конструкторе?
В конструкторе твоей синхронизированной коллекции.
AVK>>Как думаешь, почему в ICollection есть свойства IsSynchronized и SyncRoot, а в ICollection<T> уже ничего подобного нет? Z>Почему? Мне правда интересно.
См. выше.
AVK>>Почему близкий по семантике твоему примеру метод Union находится в хелпере, а не в интерфейсе IEnumerable<T>? Z>Как минимум по причине обратной совместимости.
Ага, так значит хелперы таки работают в плане обратной совместимости? Хорошо, тогда следующий вопрос — почему аналогичную ситуацию мы видим в классе Queryable? Ведь IQueryable до этого в фреймворке не было, следовательно никаких проблем с совместимосттью нет?
Z>Я не выдумывал правил по выносу, я только показал, что правило выдуманное IB для улучшения расширяемости может сработать с точностью до наоборот.
Пока что ты этого не показал.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Начинается изменение задачи на ходу. Т.е. требовать в примере с Substring абсолютной конкретики можно, а тут вдруг задача с каждым сообщением меняется произвольным образом.
Где задача поменялась?
Я написал чистую правду, что не планировал показ интерфейса наружу, об этом вполне конкретно говорит модификатор internal в исходном сообщении.
Строкой ниже я написал, что произойдет, если я его таки вынесу. Если тебе кажется, что то "изменение" задачи притянуто за уши — не читай про него, текст ниже описывает проблемы которе происходя при публикации данного интерфейса и более строго типизированого AddAll который предложил ты.
AVK> Если хочешь конкретики — то твое решение никуда не годится, прозрачное и закрытое введение блокировки до добра не доводит. Завтра тебе понадобится несколько последовательных добавлений и удалений — будем вводить в базовый интерфейс метод AddAndRemoveAll? А знаешь в чем самое веселое — вариант с хелперами легко и непринужденно превращается в вариант с методами класса, причем, благодаря extension методам, еще и абсолютно прозрачно для прикладного кода, использующего ISet, на уровне исходников (т.е. код надо просто перекомпилировать).
Точно также легко и непринужденно с совместимостью на уровне исходников метод при необходимости выносится в extensions. О чем мы тогда спорим?
AVK>А вот если ты вкрячил AddAll в ISet, а в результате никаких синхронизаций не понадобилось, выкинуть его оттуда будет ой как непросто. И придется несчастным реализаторам копипейстить один и тот же код твоих AddAll. Так где здесь унылое гавно в дизайне?
Унаследоваться от AbstractSet им религия помешает?
AVK>>>Кого списали в канализацию? Я лично никого в канализацию не списывал. Z>>А как же это? AVK>>>Неа, унылое гавно, это когда в уже существующих методах вдруг появляется блокировка AVK>То есть факт, который работал в нашу пользу — это когда в уже существующих методах вдруг появляется блокировка?
Нет, более общий факт, когда начинает требоваться полиморфное поведение вынесенного наружу метода.
AVK>Слушай, мне совершенно не интересно доказывать, что я прав. Если тебе так будет приятно — я признаю что прав ты и больше не буду с тобой спорить.
Признай Я только за.
Здравствуйте, Ziaw, Вы писали:
Z>Где задача поменялась?
То был метод с единственной реализацией, то вдруг синхронизация откуда то вылезла.
Z>Я написал чистую правду, что не планировал показ интерфейса наружу, об этом вполне конкретно говорит модификатор internal в исходном сообщении.
Но при этом ты решил все условия задачи сразу не писать. Наверное надеялся поймать.
Понимаешь — примеры хороши, если они что то иллюстрируют. А вот примеры в качестве доказательств обобщенных вещей — никуда не годятся. Именно потому что они тут же сводят все обсуждение с принципа на конкретный код.
Т.е., в твоем случае, конструктивным было бы сразу продемонстрировать исходный пример, появление синхронизации, возможные примеры решения в случае с хелперами и большим интерфейсом. Вот тогда можно было бы обсудить конструктивно — что и как можно сделать в каждом случае.
Z>Точно также легко и непринужденно с совместимостью на уровне исходников метод при необходимости выносится в extensions.
Увы нет. Добавление элемента в контракт всегда проще, чем удаление его оттуда.
Z>Унаследоваться от AbstractSet им религия помешает?
То есть мы еще и абстрактный класс нарисуем? И тесно увяжем все реализации с твоим AbstractSet? А если пользюковые классы имеют свою иерархию наследования, куда твой AbstractSet вкрячить не получится? Например от MBR надо наследоваться?
AVK>>То есть факт, который работал в нашу пользу — это когда в уже существующих методах вдруг появляется блокировка? Z>Нет, более общий факт, когда начинает требоваться полиморфное поведение вынесенного наружу метода.
Появление полиморфизма там, где его до этого не было — это серьезнейшая перестройка кода, ловить копейки на этом фоне уже бессмысленно. Опять же — буквально недавно ты все конкретики требовал. А теперь что — конкретика уже не катит?
Z>Признай Я только за.
Хорошо, ты прав. Теперь можно обходится без подобных приемчиков?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Понимаешь, блокировка такая штука, что ее не стоит прятать внутри. Иначе такие суперрешения будут — мама не горюй, перерасход на блокировках и дедлоки. Только внешний код знает, как правильно расставлять локи, не надо это внутрь коллекций запихивать. Максимум что тут можно сделать — ввести специальную синхронизированную коллекцию с методами, находящимися в публичном интерфейсе класса, но не в ISet, либо отдельный хелпер, увязанный, опять же, не с ISet, а с конкретным синхронизированным классом. Именно так устроен CDS (Coordination Data Structures), в отличие от кривого решения в первом фреймворке.
Пример был просто придуман на скорую руку для демонстрации появления требования приводящего к полиморфному поведению вынесенного метода. Или такое требование реально только в высосаных из пальца ситуациях?
За советы по поводу синхронизации спасибо, познавательно.
AVK>>>А что, если для одной и той же коллекции нужен и с блокировкой доступ и без онной (как минимум в конструкторе с сигнатурой (IEnumerable<T>) блокировка не нужна)? Вводить специальное свойство еще — IsLockActive? Z>>В чьем конструкторе? AVK>В конструкторе твоей синхронизированной коллекции.
А зачем в конструкторе вызывать виртуальный метод?
AVK>Ага, так значит хелперы таки работают в плане обратной совместимости?
Я гдето утверждал обратное? Всегда считал их хорошим средством расширения.
AVK>Хорошо, тогда следующий вопрос — почему аналогичную ситуацию мы видим в классе Queryable? Ведь IQueryable до этого в фреймворке не было, следовательно никаких проблем с совместимосттью нет?
Потому, что в случае экземплярной реализации всем реализациям пришлось бы наследоваться от абстрактного класса, а это не всегда приемлемо.
Z>>Я не выдумывал правил по выносу, я только показал, что правило выдуманное IB для улучшения расширяемости может сработать с точностью до наоборот. AVK>Пока что ты этого не показал.
Буду дальше стараться. Для меня это очевидно.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Еще как упала. Раз уж была упомянута STL: хороший пример — разделение алгоритмов и контейнеров: все что необходимо, так это предоставить пару итераторов — все остальное начнет работать автоматически. То же и для алгоритмов поверх любых контейнеров. А что будет если слить все в кучу ?
.Net Framework Class Library?
ЮЖ>Здесь множество причин для изменений. Я про это.
Ты как-то криво обозвал SRP. SRP как и любой другой принцип не самоцель и не надо под алгом больбы с классами занимающимися сразу несколькими задачами измельчать всё что можно и нельзя. Есть классы, которые не просто содержат сотню методов, это абсолютно нормально в предметной области их использования.
ЮЖ>Тогда зачем вообще раскидывать? — один класс на библиотеку, да и не мучиться...
Раскидывать надо не ради процесса раскидывания, а ради построения понятного, удобного интерфейса.
ЮЖ>Из нескольких классов, которые можно использовать независимо, проще собирать то что необходимо.
Их нельзя использовать независимо. В хелпер функции выносили потому что можно, а не по логическим признакам предметной области. То есть ничего не упростили.
A>>И получить STL где у сттроки нет нормального find ЮЖ>А нормальный это какой ? с регэкспами и поиском через гугл?
Досстаточно регэкспов.
ЮЖ>Может быть. В зависимости от задачи. Со строками проблема в том, что там слишком много потенциально настраиваемых моментов, и при попытке свалить все в кучу получится вышеупомянутый реструктуризатор.
Да вот System.String как-то всем хватает.
ЮЖ>Не надо доводить до маразма кажущееся "удобство" в виде автокомплита. Иначе чем объяснить изначальное существование в классе методов которых там не должно быть?
Соответствие логике предметной области.
ЮЖ>Ничего против автокомплита не имею, но неужели это действительно может рассматриваться как критерий выбора библиотеки ?
Близость объектной модели и реалий предметной области — критерий.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Ziaw, Вы писали:
AVK>Понимаешь — примеры хороши, если они что то иллюстрируют. А вот примеры в качестве доказательств обобщенных вещей — никуда не годятся. Именно потому что они тут же сводят все обсуждение с принципа на конкретный код. AVK>Т.е., в твоем случае, конструктивным было бы сразу продемонстрировать исходный пример, появление синхронизации, возможные примеры решения в случае с хелперами и большим интерфейсом. Вот тогда можно было бы обсудить конструктивно — что и как можно сделать в каждом случае.
Ок. Признаю, что так было бы честнее. Но я не знал бы что возразить на аргумент типа "сам дурак раз так спроектировал".
Z>>Точно также легко и непринужденно с совместимостью на уровне исходников метод при необходимости выносится в extensions. AVK>Увы нет. Добавление элемента в контракт всегда проще, чем удаление его оттуда.
Чем?
AVK>Хорошо, ты прав. Теперь можно обходится без подобных приемчиков?
Постараюсь.
Здравствуйте, Ziaw, Вы писали:
Z>Пример был просто придуман на скорую руку для демонстрации
Вот уж нет. Если бы он был для демонстрации — ты бы сразу свою мысль продемонстрировал, а не тянул бы кота за хвоста, расписывая свой пример на несколько сообщений.
Z> появления требования приводящего к полиморфному поведению вынесенного метода
Посчитай — сколько сообщений между твоим начальным примером и появлением слова "полиморфный". Вот это и называется — меняем пример на ходу.
Z>За советы по поводу синхронизации спасибо, познавательно.
Зато абсолютно не в тему. Что и ожидалось.
AVK>>В конструкторе твоей синхронизированной коллекции. Z>А зачем в конструкторе вызывать виртуальный метод?
То есть у тебя будут две реализации AddAll внутри, одна виртуальная, другая нет? Да и почему виртуальный? Для реализации интерфейса виртуальность не обязательна, интерфейс свою виртуальность добавит.
AVK>>Ага, так значит хелперы таки работают в плане обратной совместимости? Z>Я гдето утверждал обратное?
Что тогда ты пытаешься доказать?
AVK>>Хорошо, тогда следующий вопрос — почему аналогичную ситуацию мы видим в классе Queryable? Ведь IQueryable до этого в фреймворке не было, следовательно никаких проблем с совместимосттью нет? Z>Потому, что в случае экземплярной реализации всем реализациям пришлось бы наследоваться от абстрактного класса
IQueryable, как несложно догадаться из названия, это интерфейс и никакого наследования от абстрактного класса он не требует.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
Z>>>Точно также легко и непринужденно с совместимостью на уровне исходников метод при необходимости выносится в extensions. AVK>>Увы нет. Добавление элемента в контракт всегда проще, чем удаление его оттуда. Z>Чем?
Тем, что про новый член никто не знает, и его добавление по сути совсем не меняет семантику использующего кода (если только не умудрились дать возможность с его помощью поломать инварианты). А вот удаление метода безусловно меняет семантику тех кусков, которые его использовали, и над этими кусками уже надо думать мозгой, что делать.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
AVK>Что тогда ты пытаешься доказать?
что правило выдуманное IB для улучшения расширяемости может сработать:
1. для ухудшения читабельности кода (занятие бесполезное, поскольку измерить читабельность нечем), а закладываемая расширяемость никогда не появится. (для всех случаев выноса кроме экстеншен методов)
2. для ухудшения расширяемости
AVK>>>Хорошо, тогда следующий вопрос — почему аналогичную ситуацию мы видим в классе Queryable? Ведь IQueryable до этого в фреймворке не было, следовательно никаких проблем с совместимосттью нет? Z>>Потому, что в случае экземплярной реализации всем реализациям пришлось бы наследоваться от абстрактного класса AVK>IQueryable, как несложно догадаться из названия, это интерфейс и никакого наследования от абстрактного класса он не требует.
Я в курсе, даже пытался его реализовать.
Имелось в виду, что в случае включения всех методов в контракт интерфейса в каждой реализации пришлось бы либо копипастить имплементацию либо наследоваться от класса где они уже есть.
Здравствуйте, Ziaw, Вы писали:
Z>1. для ухудшения читабельности кода
Пример этого не продемонстрировал.
Z> (занятие бесполезное
Ага, особенно если учесть, что код с использванием extension методов идентичен коду использования instance методов вплоть до символа.
Z>2. для ухудшения расширяемости
Пример этого не продемонстрировал.
AVK>>IQueryable, как несложно догадаться из названия, это интерфейс и никакого наследования от абстрактного класса он не требует. Z>Я в курсе, даже пытался его реализовать. Z>Имелось в виду, что в случае включения всех методов в контракт интерфейса в каждой реализации пришлось бы либо копипастить имплементацию либо наследоваться от класса где они уже есть.
Ну вот, ты и сам можешь ответить на свой вопрос, почему раздувание твоего гипотетического ISet — идея совсем не замечательная.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Ziaw, Вы писали:
Z>>>>Точно также легко и непринужденно с совместимостью на уровне исходников метод при необходимости выносится в extensions. AVK>>>Увы нет. Добавление элемента в контракт всегда проще, чем удаление его оттуда. Z>>Чем?
AVK>Тем, что про новый член никто не знает, и его добавление по сути совсем не меняет семантику использующего кода (если только не умудрились дать возможность с его помощью поломать инварианты). А вот удаление метода безусловно меняет семантику тех кусков, которые его использовали, и над этими кусками уже надо думать мозгой, что делать.
Что-то я туплю, как может перенос метода в экстеншен поменяет семантику кода? null в качестве аргумента? так можно добавить проверку и кинуть экзепшен. может быть рефлекшен или приколы из области этюдов nikov'а?
Здравствуйте, AndrewVK, Вы писали:
Z>>1. для ухудшения читабельности кода AVK>Пример этого не продемонстрировал.
Я тебе дам пример. Есть некоторый класс HtmlElement, название говорит само за себя, это базовый класс HTML движка. Не важно какого: IE, Mozilla, Opera, везде практически одно и то же. В минимальной реализации он такой.
class HtmlElement
{
private IntPtr _handle;
public HtmlElement(IntPtr handle)
{
this._handle = handle;
}
public IntPtr Handle
{
get { return this._handle; }
}
}
Здравствуйте, adontz, Вы писали:
A>Я тебе дам пример. Есть некоторый класс HtmlElement
О, веб-формсы. Мою любимый пример как делать не надо. Но тут тебе лучше у Синклера поспрошать, он тебе более качественно объяснить, почему. Я в вебе не очень хорошо разбираюсь.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Ziaw, Вы писали:
Z>>1. для ухудшения читабельности кода AVK>Пример этого не продемонстрировал. AVK>Ага, особенно если учесть, что код с использванием extension методов идентичен коду использования instance методов вплоть до символа.
Специально же написал что пункт один не относится к выносу в ЕМ. Как только задача сводится к выносу в EM этот пункт не играет.
Z>>2. для ухудшения расширяемости AVK>Пример этого не продемонстрировал.
И всетаки появление полиморфности вполне реальная ситуация, решение проблемы усложняется.
AVK>Ну вот, ты и сам можешь ответить на свой вопрос, почему раздувание твоего гипотетического ISet — идея совсем не замечательная.
Когда в перспективе вероятен полиморфный метод я не буду смотреть, что сейчас использует реализация, она вполне может измениться. По крайней мере одну поправку к правилу для себя я нашел.
Здравствуйте, AndrewVK, Вы писали:
AVK>О, веб-формсы. Мою любимый пример как делать не надо. Но тут тебе лучше у Синклера поспрошать, он тебе более качественно объяснить, почему. Я в вебе не очень хорошо разбираюсь.
Андрей, я ещё раз повторю, никто не говорит что это хороший дизайн. Ни коим разом. Это плохой дизайн и можно придумать дизайн, который формально лучше. Проблема критиков в том, что хороший дизайн на фиг никому не нужен. Можно писать исследовательские статье на тему какое говно DOM утверждённый W3C, но DOM W3C это предметная облать и любое несоответсвие между объектной моделью системы автоматизации браузера и DOM W3C — проблема. Пытаясь улучшить дизайн, улучшатели меняют не объектную модель, а предметнубю область.
Здравствуйте, adontz, Вы писали:
A>Андрей, я ещё раз повторю, никто не говорит что это хороший дизайн. Ни коим разом. Это плохой дизайн и можно придумать дизайн, который формально лучше.
Не только формально. Вот погляди на WPF и сравни его с винформсами, скажем тот же класс Control — разница поразительная. не так ли. Хотя и в WPF не все хорошо в этом плане, имхо.
A> Проблема критиков в том, что хороший дизайн на фиг никому не нужен.
Точно, нужен плохой.
A> Можно писать исследовательские статье на тему какое говно DOM утверждённый W3C
XML DOM то? Говно и есть.
A> Пытаясь улучшить дизайн, улучшатели меняют не объектную модель, а предметнубю область.
И как изменилась предметная область у XDocument по сравнению с XmlDocument?
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
Z>Специально же написал что пункт один не относится к выносу в ЕМ. Как только задача сводится к выносу в EM этот пункт не играет.
То есть в ответ на вопрос, зачем твой пример, ты ответил тем, что на самом деле ответом не яляется. Я чего то уже теряю нить рассуждения.
AVK>>Пример этого не продемонстрировал. Z>И всетаки появление полиморфности вполне реальная ситуация, решение проблемы усложняется.
Появление или пропадание полиморфности находится абсолютно за рамками принципа Мейерса по вынесению методов.
Z>Когда в перспективе вероятен полиморфный метод я не буду смотреть, что сейчас использует реализация, она вполне может измениться. По крайней мере одну поправку к правилу для себя я нашел.
Это не поправка к правилу, это его следствие. Необходимость полиморфизма однозначно требует помещения метода в основной контракт и правилу никак не противоречит.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>>Еще как упала. Раз уж была упомянута STL: хороший пример — разделение алгоритмов и контейнеров: все что необходимо, так это предоставить пару итераторов — все остальное начнет работать автоматически. То же и для алгоритмов поверх любых контейнеров. А что будет если слить все в кучу ?
A>.Net Framework Class Library?
Нет, там как раз порезано на множество классов, что лучше чем один.
ЮЖ>>Здесь множество причин для изменений. Я про это.
A>Ты как-то криво обозвал SRP.
Почему?
A>SRP как и любой другой принцип не самоцель и не надо под алгом больбы с классами занимающимися сразу несколькими задачами измельчать всё что можно и нельзя.
Интерфейс изначально должен быть минимально полным для выполненя своих обязанностей. Уже потом он может обрастать хелперами.
A>Есть классы, которые не просто содержат сотню методов, это абсолютно нормально в предметной области их использования.
Если это минимально полный набор, то пожалуйста.
... A>>>И получить STL где у сттроки нет нормального find ЮЖ>>А нормальный это какой ? с регэкспами и поиском через гугл?
A>Досстаточно регэкспов.
Реализуются через существующий интерфейс. Бывают разные, да и нужны далеко не всем. + тут встает вопрос об "объектной модели" строк.
ЮЖ>>Может быть. В зависимости от задачи. Со строками проблема в том, что там слишком много потенциально настраиваемых моментов, и при попытке свалить все в кучу получится вышеупомянутый реструктуризатор.
A>Да вот System.String как-то всем хватает.
Я имел ввиду положение дел в С++
ЮЖ>>Не надо доводить до маразма кажущееся "удобство" в виде автокомплита. Иначе чем объяснить изначальное существование в классе методов которых там не должно быть? A>Соответствие логике предметной области.
Как может соответствовать логике предметной области то, что ей не соответствует ? ну вот например, нет у класса dog метода 'save(database&)'. Нету.
А писать, да удобнее: 'my_dog.save(db)' с автокомплитом так вообще прелесть =)
ЮЖ>>Ничего против автокомплита не имею, но неужели это действительно может рассматриваться как критерий выбора библиотеки ? A>Близость объектной модели и реалий предметной области — критерий.
Я говорю не о том чтобы вырезать методы из класса, мотивируя разделением обязанностей, а о том чтобы изначально не создавать лишние методы. Я не могу предвидеть все варианты использования(если речь про библиотеку), но я могу только предоставить минимально необходимый интерфейс к одной логической сущности.
Здравствуйте, AndrewVK, Вы писали:
AVK>Не только формально. Вот погляди на WPF и сравни его с винформсами, скажем тот же класс Control — разница поразительная. не так ли. Хотя и в WPF не все хорошо в этом плане, имхо.
Что не мешает HTML + CSS по декларативным возможностям рвать WPF. Ну и чего было столько мучаться когда старая система с плохой архитектурой оказалась мощнее новой системы с хорошей архитектурой? Да ещё и шустрее.
A>> Проблема критиков в том, что хороший дизайн на фиг никому не нужен. AVK>Точно, нужен плохой.
Нужен знакомый.
A>> Можно писать исследовательские статье на тему какое говно DOM утверждённый W3C AVK>XML DOM то? Говно и есть.
HTML, будь внимательнее.
A>> Пытаясь улучшить дизайн, улучшатели меняют не объектную модель, а предметнубю область. AVK>И как изменилась предметная область у XDocument по сравнению с XmlDocument?
Здравствуйте, AndrewVK, Вы писали:
AVK>То есть в ответ на вопрос, зачем твой пример, ты ответил тем, что на самом деле ответом не яляется. Я чего то уже теряю нить рассуждения.
Ответ был на AVK>Что тогда ты пытаешься доказать?
Я ответил, что я пытаюсь доказать в этой ветке вообще, а не каким-то примером в частности.
Z>>Когда в перспективе вероятен полиморфный метод я не буду смотреть, что сейчас использует реализация, она вполне может измениться. По крайней мере одну поправку к правилу для себя я нашел. AVK>Это не поправка к правилу, это его следствие. Необходимость полиморфизма однозначно требует помещения метода в основной контракт и правилу никак не противоречит.
Не необходимость, а вероятность возникновения.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
A>>.Net Framework Class Library? ЮЖ>Нет, там как раз порезано на множество классов, что лучше чем один.
По сравнению с STL сплошной монолит. Поиск в каждом контейнере свой, обощённый интерфейс очень специализированный, итераторов нет.
A>>Ты как-то криво обозвал SRP. ЮЖ>Почему?
Ну он стал трудноузнаваемым
ЮЖ>Интерфейс изначально должен быть минимально полным для выполненя своих обязанностей. Уже потом он может обрастать хелперами.
А обязанности беруться не из деталей реализации, а из предметной области.
ЮЖ>Если это минимально полный набор, то пожалуйста.
Минимально полный не с точки зрения реализации, а с точки зрения предметной области.
A>>Досстаточно регэкспов. ЮЖ>Реализуются через существующий интерфейс. Бывают разные, да и нужны далеко не всем. + тут встает вопрос об "объектной модели" строк.
Ну вот в .Net есть всего один вид и даже не такой как другие и ничего. Тут главное стандарт. В Си++ сколько было вид строк? А в Паскале? Не надо делать супер-класс, надо делать достаточно хороший и стандартный.
A>>Да вот System.String как-то всем хватает. ЮЖ>Я имел ввиду положение дел в С++
В Си++ со строками хронический бардак.
ЮЖ>Как может соответствовать логике предметной области то, что ей не соответствует ? ну вот например, нет у класса dog метода 'save(database&)'. Нету. ЮЖ>А писать, да удобнее: 'my_dog.save(db)' с автокомплитом так вообще прелесть =)
Не передёргивай. Можно писать db.save(my_dog) и это будет нормально, потому что DB это контейнер Dog. Я против, например, Bark.By(my_dog). Это может быть удобно в реализации, но страдательный залог непривычен для восприятия.
A>>Близость объектной модели и реалий предметной области — критерий. ЮЖ>Я говорю не о том чтобы вырезать методы из класса, мотивируя разделением обязанностей, а о том чтобы изначально не создавать лишние методы. Я не могу предвидеть все варианты использования (если речь про библиотеку), но я могу только предоставить минимально необходимый интерфейс к одной логической сущности.
Хорошо, только вот разбиение логической группы методов на разные классы, которое тут демонстрировалось как "хорошо", мною вопронимается как диверсия.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Нет, там как раз порезано на множество классов, что лучше чем один.
Неужели ты думаешь, найдется хоть один человек который начнет доказвать обратное?
Практически каждый сторонник выноса всего подряд из класса кто приходит в эту ветку
сразу придумывает это неотразимый аргумент.
И пытается сделать из сторонников подумать еще о чем-то кроме формального правила борцов за бездумное засовывание всего подряд в один класс.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Юрий Жмеренецкий, Вы писали:
A>>>.Net Framework Class Library? ЮЖ>>Нет, там как раз порезано на множество классов, что лучше чем один.
A>По сравнению с STL сплошной монолит. Поиск в каждом контейнере свой, обощённый интерфейс очень специализированный, итераторов нет.
Значит еще резать надо =)
ЮЖ>>Интерфейс изначально должен быть минимально полным для выполненя своих обязанностей. Уже потом он может обрастать хелперами. A>А обязанности беруться не из деталей реализации, а из предметной области.
А я где-то утверждал обратное ?
ЮЖ>>Если это минимально полный набор, то пожалуйста. A>Минимально полный не с точки зрения реализации, а с точки зрения предметной области.
Это само собой
... A>>>Близость объектной модели и реалий предметной области — критерий. ЮЖ>>Я говорю не о том чтобы вырезать методы из класса, мотивируя разделением обязанностей, а о том чтобы изначально не создавать лишние методы. Я не могу предвидеть все варианты использования (если речь про библиотеку), но я могу только предоставить минимально необходимый интерфейс к одной логической сущности.
A>Хорошо, только вот разбиение логической группы методов на разные классы, которое тут демонстрировалось как "хорошо", мною вопронимается как диверсия.
По моему здесь демонстрировалось несколько иное: попытка добавления к интерфесу(изменение предметной области) методов, которые выразимы в терминах этого интерфейса.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
A>>Хорошо, только вот разбиение логической группы методов на разные классы, которое тут демонстрировалось как "хорошо", мною вопронимается как диверсия. ЮЖ>По моему здесь демонстрировалось несколько иное: попытка добавления к интерфесу(изменение предметной области) методов, которые выразимы в терминах этого интерфейса.
Нет-нет, пречитай внимательнее, тут собрались Свидетели Правила.
Кромсать всегда, кромсать везде,
До строк последних донца,
Кромсать и никаких идей,
Вот лозунг мой и Солнца.
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>>Нет, там как раз порезано на множество классов, что лучше чем один. Z>Неужели ты думаешь, найдется хоть один человек который начнет доказвать обратное?
Сколько уже в топике сообщений ? =)
Z>Практически каждый сторонник выноса всего подряд из класса
Я не сторонник "выноса всего подряд", я сторонник не "вноса" всего подряд. Это разные вещи, ситуции когда надо выносить все подряд зачастую вообще не лечатся.
Z>кто приходит в эту ветку сразу придумывает это неотразимый аргумент.
Это оппоненты пытаются находить в сказанном обязательные "придуманные неотразимые аргументы".
Z>И пытается сделать из сторонников подумать еще о чем-то кроме формального правила борцов за бездумное засовывание всего подряд в один класс.
А сам факт существования всяческих SRP, OCP, DIP и т.п, ни на что не наводит ?
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>А сам факт существования всяческих SRP, OCP, DIP и т.п, ни на что не наводит ?
Наводит на мысль, что единого метода оценки качества кода как не было так и нет, а значит применять любой из существующих надо аккуратно. Справедливости ради надо заметить, что методы становяться всё сложнее и их область применения шире. Однако, до совершества всё равно очень далеко.
Здравствуйте, adontz, Вы писали:
A>Что не мешает HTML + CSS по декларативным возможностям рвать WPF.
Мы обсуждаем ОО API, а не декларативные возможности.
A> Ну и чего было столько мучаться когда старая система с плохой архитектурой оказалась мощнее новой системы с хорошей архитектурой? Да ещё и шустрее.
Рома, какое это имеет отношение к обсуждаемому вопросу.
A>>> Проблема критиков в том, что хороший дизайн на фиг никому не нужен. AVK>>Точно, нужен плохой.
A>Нужен знакомый.
Знакомый, простите, кому?
A>>> Можно писать исследовательские статье на тему какое говно DOM утверждённый W3C AVK>>XML DOM то? Говно и есть.
A>HTML, будь внимательнее.
, на которое я отвечал, слово HTML? Это тебе надо повнимательнее, мысли я читать не умею. И с HTML DOM, который в каком нибудь JavaScript доступен, мне знаком плохо и твои примеры мне ни о чем не говорят.
A>>> Пытаясь улучшить дизайн, улучшатели меняют не объектную модель, а предметнубю область. AVK>>И как изменилась предметная область у XDocument по сравнению с XmlDocument?
A>Я про HTML.
А с ХML та же ситуация — XML DOM унылое гавно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
AVK>>Это не поправка к правилу, это его следствие. Необходимость полиморфизма однозначно требует помещения метода в основной контракт и правилу никак не противоречит. Z>Не необходимость, а вероятность возникновения.
Мне вот кажется, что твоя ошибка в следующем — в начале ты зачем то абсолютизируешь обсуждаемое правило, а потом с этой абсолютизацией споришь. Еще раз процитирую его:
If you're writing a function that can be implemented as either a member or as a non-friend non-member, you should prefer to implement it as a non-member function.
На самом деле это правило — вторично. То есть оно не определяет внутренний дизайн методов. Там нет об этом ни слова. Любая, я повторяю, любая причина, по которой метод не может быть оторван от класса без изменения реализации этого метода, является причиной того, что этот метод должен остаться в классе. И уж если ты решил озаботиться полиморфизмом заранее — флаг тее в руки. Только это никоим образом это самое правило не нарушает.
Что же касается обоснованности подобного предварительного полиморфизма — это отдельный вопрос, за рамками правила. С моей точки зрения этого делать не стоит ни в коем случае — полиморфные структуры очень тесно интегрируются в дизайн приложения, и принимать такие глобальные решения по дизайну на всякий случай — прямой путь к неоправданно сложному дизайну.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, IB, Вы писали:
A>>Ок, за исключением одной вещи -- смерти человека. IB>Ну пока код для игрушечных приложений пишется — оно конечно да, исправить можно все. Аты представляешь какой кровью дается обратная совместимость с предыдущими версиями API своего же продукта?
Так что вообще свой фреймворк не развивать? Пусть топчется на ровном месте -- исправляем баги и увеличиваем перформанс?
Посмотри на .net FW нет там обратной совместимости.
Посмотри на DevExpress: они даже номер версии в название сборки включают -- чтобы не было бездумного (автоматического) перехода на новую версию библиотеки.
Общий сценарий замены таков:
1) Потребовалось заменить существующую реализаци, сделать новую более эффективную, удобную, ...
2) Реализовываешь ее параллельно с первоначальной
3) Помечаешь старую реализацию Obsolete и останавливаешь работу над ней -- все пользователи старого варианта получают предупреждения: "реализация устарела, в один прекрасный момент ее может совсем не стать, используйте то-то и то-то взамен"
4) Несколько версий библиотеки выпускаются с Obsolete, чтобы дать пользователям время перейти на новый дизайн
5) Методы удаляются -- кто не заменил -- сам дурак, было дано куча времени.
IB>Я бы посмотрел, как ты будешь исправлять свой код, на котором кастомер уже пару решений построил и впарил третьему клиенту.
Построил? -- Замчательно.
Впарил клиенту? -- Значит с текущей версией библиотеки его приложение работает неплохо.
Хочет исползовать новую версию библиотеки? -- А, зачем раз все работает и продается?
Хочет развивать приложений? -- Ну тогда пусть изменяет пользователей измененного класса -- ему же лучше, так как новый дизайн удобнее.
A>> Да, есть масса общепринятых практик. Твое правило к которым очевидно не относится. IB>Очевидно относится, так как основано на тех же самых практиках.
Раз мы ведем этот спор, то для нас это неочевидно.
Есть другой вариант этого правила
, вот он мне нравится. В первую очередь некатегоричностью в отличии от твоего:
If you're writing a function that can be implemented as either a member or as a non-friend non-member, you should prefer to implement it as a non-member function.
Мало того, что should -- наименее "должный" из всех английских глаголов "должен" (should, have to, must), так еще тут фигурирует слово "предпочесть".
Против этого правила я ничего не могу возразить так как оно действительно соответствует истине: "у вас должны быть причины, чтобы поместить такой метод в класс". Таких причин в этом топике было дано много и много еще будет дано.
A>>Программист страдающий амнезией? Занятно. IB>Это не занятно, это жизнь.
Общие слова. Иван, предоставь, пожалуйста пример когда прогаммисту нужно метаться от одного варианта к другому по несколько раз в неделю в течении месяца.
Здравствуйте, AndrewVK, Вы писали:
AVK>Мне вот кажется, что твоя ошибка в следующем — в начале ты зачем то абсолютизируешь обсуждаемое правило, а потом с этой абсолютизацией споришь.
Я в шоке от таких приемов ведения дискуссии.
Абсолютизировали его вы:
IB>Это не "прыжек в сторону". Андрюша уже писал об этом, как, впрочем, и я. Принцип сформулирован очень четко. Если метод использует только публичный контракт для работы с классом, значит он должен находиться вне класса. IB>Уже использует, понимаешь? Это данность, его так задизайнили, не важно по каким причинам. Если по каким либо причинам методу нужно использовать приватные данные, не важно по каким, дизайн ли, производительность ли, еще что-то, то его место внутри. IB>Все очень четко, конкретно, понятно и прозрачно. В данном случае части методов, из соображений производительности, нужно иметь доступ к внутреннему состоянию, значит их место внутри класса.
И я спорю именно с вашим возведением в абсолют, не с тараканами в моей голове которые это сделали.
Я 10 раз писал, что не согласен только с безусловностью данного правила.
Здравствуйте, Aikin, Вы писали:
A>Так что вообще свой фреймворк не развивать?
Где я такое сказал?
A>Посмотри на .net FW нет там обратной совместимости.
Ты серьезно?
A>5) Методы удаляются -- кто не заменил -- сам дурак, было дано куча времени. Этот "дурак" тебе платит деньги и каждый breaking change потенциально грозит потерей клиента.
A>Построил? -- Замчательно. A>Впарил клиенту? -- Значит с текущей версией библиотеки его приложение работает неплохо. A>Хочет исползовать новую версию библиотеки? -- А, зачем раз все работает и продается? A>Хочет развивать приложений? -- Ну тогда пусть изменяет пользователей измененного класса -- ему же лучше, так как новый дизайн удобнее.
Я уже расписывал Роме, как такой сценарий выглядит на практике. Тебе повторить?
A>Раз мы ведем этот спор, то для нас это неочевидно.
Для меня очевидно, что этот спор вы ведете уже из чистого упрямства, потому что возразить по делу нечего но все еще хочется.
A> Таких причин в этом топике было дано много
Таких причин, кроме "удобнее" не было дано ни одной. И это "удобнее" весьма сомнительный аргумент, в первую очередь потому, что еще не известно что удобнее.
A> и много еще будет дано.
Все обещаете...
A>Иван, предоставь, пожалуйста пример когда прогаммисту нужно метаться от одного варианта к другому по несколько раз в неделю в течении месяца.
Как ты себе представляешь этот пример? Подними историю любого своего приложения, и если будешь честен перед собой, то увидишь ровно эту картину..
Здравствуйте, adontz, Вы писали:
A>А я не сдаю назад, я же говорю, тут только ты впадаешь в крайности.
Причем тут мои крайности, ты тему-то не меняй..
A> На глазок ли? Ну не наобум, но да, в каждом конкретном случае решение может приниматься индивидуально, на основе личного опыта.
Ну, то есть, на глазок..
A>Конфигурационные файлы определённого формата могут быть непривычны.
У тебя проблемы с XML файлами? В третий раз спрашиваю, App.config — не жмет?
A>А за процесс обучения кто заплатил? КПСС?
Тот же, кто заплатит за изнурительное выпиливание по библиотеке с кривым дизайном и отлов в ней невынужденных ошибок.
, на которое я отвечал, слово HTML? Это тебе надо повнимательнее, мысли я читать не умею. И с HTML DOM, который в каком нибудь JavaScript доступен, мне знаком плохо и твои примеры мне ни о чем не говорят.
Здравствуйте, IB, Вы писали:
A>>Конфигурационные файлы определённого формата могут быть непривычны. IB>У тебя проблемы с XML файлами? В третий раз спрашиваю, App.config — не жмет?
То есть разницы между форматом XML и форматом конфигурационного файлы ты в упор не видишь.
A>>А за процесс обучения кто заплатил? КПСС? IB>Тот же, кто заплатит за изнурительное выпиливание по библиотеке с кривым дизайном и отлов в ней невынужденных ошибок.
Ай, неувязочка. За библиотеку платят один раз, а за обучение много раз.
Здравствуйте, AndrewVK, Вы писали:
AVK>Любая, я повторяю, любая причина, по которой метод не может быть оторван от класса без изменения реализации этого метода, является причиной того, что этот метод должен остаться в классе.
Вобщем, AndrewVK и IB, обсуждение вы слили. Ваша позиция постоянно меняется. На фоне общего хамства и угроз бана пропало всякое желание кому-то что-то объяснять. А всё что я хотел сказать по существу я сказал и так.
Здравствуйте, adontz, Вы писали:
A>>>Нужен знакомый. AVK>>Знакомый, простите, кому?
A>Тому, кто будет использовать.
То есть всем потенциальным пользователям? Отлично, ничего не скажешь.
A>HTML был на два сообщения выше и очень много раз.
Ну вот и не надо его приплетать, либо сразу оговаривай, о чем ты речь ведешь. По остальному, я так понимаю, возражений нет?
A>Ты уже через два сообщения забыл тему обсуждения?
Я ничего не забывал. Два сообщения назад никакого DOM еще не упоминалось.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Ziaw, Вы писали:
Z>И я спорю именно с вашим возведением в абсолют
Нет там никакого возведения. В приведенной цитате наоборот — явно упоминуто, что причины неважны.
Z>Я 10 раз писал, что не согласен только с безусловностью данного правила.
Не путай безусловность и возведение в абсолют, это не одно и то же. Правило обязано выполняться практически всегда, но это совсем не означает, что это единственное правилдо, которое определяет разнесение по методам. Вот в УК РФ — за грабеж тебя обязательно посадят, но это не единственная причина, по которой тебя могу посадить.
... << RSDN@Home 1.2.0 alpha 4 rev. 1095 on Windows Vista 6.0.6001.65536>>
Здравствуйте, Aikin, Вы писали:
<...> A>Против этого правила я ничего не могу возразить так как оно действительно соответствует истине: "у вас должны быть причины, чтобы поместить такой метод в класс". Таких причин в этом топике было дано много и много еще будет дано.
На все эти причины ответом будет: поведение меняется намного чаще, чем данные.
Именно поэтому так популярна SOA(по аналогии — RDBMS vs OODB), где данные вообще живут отдельно, и изменение логики их не затрагивает. Попытка добавления к классу метода(выразимого существующими методами) автоматически добавляет классу новые обязанности. Самое противное — рано или поздно наступит момент когда это зашитое поведение нужно менять — это приводит к изменению интерфейса класса, со всеми вытекающими последствиями. Есть вероятность что этот момент может и не наступить, но она очень маленькая, поскольку с течением времени эволюционирут требования, изменяется предметная область и т.д. Это то самое место где применение "классического" OOP приводит к несоизмеримым затратам при сопровождении. В условиях постоянно изменяющихся требований необходимы методы которые упрощают эти изменения. Вот тут рулят любые техники(принципы =) ), позволяющие снизить связывание взаимодействующих компонентов. Проще быть готовым(по умолчанию применяя обсуждаемое правило(и не только его) к изменениям, чем производить масштабный "рефакторинг"(а по сути исправление архитектурных ляпов, которые можно было предвидеть) в будущем.
Здравствуйте, adontz, Вы писали:
A>То есть разницы между форматом XML и форматом конфигурационного файлы ты в упор не видишь.
Я не вижу какие проблемы может создать формат файла основаный на XML, с проверкой на соответствие схеме и полным автокомплитом.
app.config в этом плане реально менее удобен — с ним проблем нет?
A>Ай, неувязочка. За библиотеку платят один раз, а за обучение много раз.
Я вот примерно представляю, где у кого неувязочка..
Если ты считаешь, что при наличии автокомплита обучение не нужно, то оно конечно да.
А если по уму, то достаточно один раз принцип выучить, чем корячиться над каждой библиотекой.. Тяжело в учении — легко в продакшене.. =)
Здравствуйте, adontz, Вы писали:
A>Где я говорил это?
Прямо в предыдущем сообщении.. ) Если все понапихать в класс, то обучать не надо, а обучать не надо, потому, что методы и так подскажет автокомплит => автокомплит заменяет обучение.
Здравствуйте, adontz, Вы писали:
A>Наводит на мысль, что единого метода оценки качества кода как не было так и нет, а значит применять любой из существующих надо аккуратно.
Причем тут методы оценки? Это не методы оценки, это практики, следование которым помогает лучше и качественнее поддерживать код.
Здравствуйте, Ziaw, Вы писали:
Z>Практически каждый сторонник выноса всего подряд из класса кто приходит в эту ветку Z>сразу придумывает это неотразимый аргумент.
Если практически каждому приходящему в эту ветку, при взгляде на вашу аргументацию приходит эта мысль, может надо в консерватории что-то поправить? Может это у вас с аргументами что-то не так, если наблюдается такое трогательное единодушие — не было такой идеи? )
Здравствуйте, IB, Вы писали:
IB>Если практически каждому приходящему в эту ветку, при взгляде на вашу аргументацию приходит эта мысль, может надо в консерватории что-то поправить? Может это у вас с аргументами что-то не так, если наблюдается такое трогательное единодушие — не было такой идеи? )
Вобщем-то я вышел из дискуссии. Все что можно было из нее вынести я вынес. Если мои аргументы кого-то наводят на такие мысли я бы хотел увидеть эти аргументы и логическую цепочку рассуждений ведущих к вышеизложеной идее.
Пока не увижу, буду считать это не следствием наших аргументов, а попыткой бросить первый камень который попался под руку.
Здравствуйте, IB, Вы писали:
IB>Если практически каждому приходящему в эту ветку, при взгляде на вашу аргументацию приходит эта мысль, может надо в консерватории что-то поправить? Может это у вас с аргументами что-то не так, если наблюдается такое трогательное единодушие — не было такой идеи? )
А может каждый мерит на свой аршин и привык доволить любую идею: расщепление классов, слияние, до крайности.
Здравствуйте, IT, Вы писали:
IT>Ну угадал. Локальные функции рулят Может Хейлсберг когда-нибудь родит что-нибудь подобное в шарпе.
А лямбд не достаточно?
Или это стёб?
Здравствуйте, Ziaw, Вы писали:
Z>Если мои аргументы кого-то наводят на такие мысли я бы хотел увидеть эти аргументы
Свои же собственные аргументы хочешь увидеть? Я бы тоже хотел, в этом-то и проблема.. =)
Z>и логическую цепочку рассуждений ведущих к вышеизложеной идее.
Да тут полтопика логических цепочек, на вас это не работает, я проверял.
Z>Пока не увижу, буду считать это не следствием наших аргументов, а попыткой бросить первый камень который попался под руку.
Удобно..
Здравствуйте, IB, Вы писали:
A>>Я такого не говорил, читай внимательнее. IB>О, уже от своих слов открещивается.
Исключительно от твоих галлюцинаций. И вообще хватит прыгать на словах, по существу тебе всё равно сказать нечего, а отвечать на твои нелепые зацепки за отдельные слова не интересно.
Здравствуйте, adontz, Вы писали:
A> Исключительно от твоих галлюцинаций.
Ну, я же говорил. Даешь им логическую цепочку, а они называют ее галлюцинацией и уходят от ответа => Логические цепочки не рабтают..
A> И вообще хватит прыгать на словах,
Не, это твоя прерогатива, тут я с тобой тягаться не стану..
A>по существу тебе всё равно сказать нечего,
"Как можно отвечать по существу на чьи-то галлюцинации?" (с) =))
A> а отвечать на твои нелепые зацепки за отдельные слова не интересно.
Было бы не интересно — не отвечал бы..
Здравствуйте, EvilChild, Вы писали:
IT>>Ну угадал. Локальные функции рулят Может Хейлсберг когда-нибудь родит что-нибудь подобное в шарпе. EC>А лямбд не достаточно?
В качестве локальных функций? Можно, конечно, но получается криво и косо.
EC>Или это стёб?
Попробуй сам.
Неясность изложения обычно происходит от путаницы в мыслях.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, EvilChild, Вы писали:
IT>>В качестве локальных функций? Можно, конечно, но получается криво и косо.
EC>Видимо я не понял, что понимается под локальными функциями. Можешь в 2х словах излодить своё понимание?
Аналог локальной функции на C# будет примерно такой:
1. необходимость объявления делегата (хотя в C# 3.0 можно использовать Func<...>),
2. необходимость объъявления локальной переменной,
3. раздельное объявление локальной переменной и самой функции в случае рекурсивного вызова.
В принципе близко, но всё же в C# не хватает законченности решения.
Зачем нужны локальные функции. Как раз чтобы не плодить private методы, что в контексте данного топика приводит к уменьшению связности внутри класса, т.к. декомпозицию кода на уровне метода можно производить не выходя за его пределы.
Неясность изложения обычно происходит от путаницы в мыслях.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IB, Вы писали:
A>> Таких причин в этом топике было дано много IB>Таких причин, кроме "удобнее" не было дано ни одной. И это "удобнее" весьма сомнительный аргумент, в первую очередь потому, что еще не известно что удобнее.
Это называется предпочтения. И к ним надо относиться очень и очень внимательно, понимая при этом, что у каждого они свои. Иначе получится как у JetBrains с кнопками F12, back и forward.
Неясность изложения обычно происходит от путаницы в мыслях.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>1. необходимость объявления делегата (хотя в C# 3.0 можно использовать Func<...>), IT>2. необходимость объъявления локальной переменной, IT>3. раздельное объявление локальной переменной и самой функции в случае рекурсивного вызова.
Недостатки несмертельные. Реальный только второй. Хотя немерловый вариант конечно попрямее будет.
Здравствуйте, IT, Вы писали:
IT>Это не недостатки. Это кривизна и извращенство. Как результат, фича практически не используется, кроме тех, кто пользовался ей в других языках.
По моему ты драматизируешь. Привёл крайний случай с рекурсией и на его основании делаешь такое сильное утверждение.
Без неё всё выглядит вполне нормально, не хуже Немерля.
Здравствуйте, EvilChild, Вы писали:
EC>По моему ты драматизируешь.
Я использую оба языка и вижу, что в Немерле локальные функции сами выскакивают из под "пера". В C# только когда припрёт.
EC>Привёл крайний случай с рекурсией и на его основании делаешь такое сильное утверждение. EC>Без неё всё выглядит вполне нормально, не хуже Немерля.
Рекурсия как раз наиболее частый случай, когда часть функциональности метода выносится в отдельную вспомогательную функцию.
Неясность изложения обычно происходит от путаницы в мыслях.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Рекурсия как раз наиболее частый случай, когда часть функциональности метода выносится в отдельную вспомогательную функцию.
Неужели ты так часто рекурсией в шарпе пользуешься, что тебя это напрягает?
Здравствуйте, EvilChild, Вы писали:
IT>>Рекурсия как раз наиболее частый случай, когда часть функциональности метода выносится в отдельную вспомогательную функцию. EC>Неужели ты так часто рекурсией в шарпе пользуешься, что тебя это напрягает?
Почему меня это должно напрягать? Как раз наоборот, рекурсивные алгоритмы неплохая разминка для мозгов, поэтому хочется всё сделать максимально изящно, без кривостей и извращенств.
Неясность изложения обычно происходит от путаницы в мыслях.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Почему меня это должно напрягать? Как раз наоборот, рекурсивные алгоритмы неплохая разминка для мозгов, поэтому хочется всё сделать максимально изящно, без кривостей и извращенств.
Я просто подумал, что твоё неудовольствие носит практический характер.
Если только мозги разминать мешает, то ок.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, EvilChild, Вы писали:
EC>>Я просто подумал, что твоё неудовольствие носит практический характер.
IT>Не практический, а эстетический. Грязь в коде — грязь в головах.
EC>>Если только мозги разминать мешает, то ок.
IT>А это тут при чём?
Так оно мешает в повседневной работе или только при кодировании всяких хитрых штук для развлечения?
Здравствуйте, Lloyd, Вы писали:
Р>>Ты лучше объясни мне, нафига оно надо? Ну, посчитали, и чего с ним Р>>делать-то? LOC тоже очень хорошо формализировался. Вот только толку от Р>>него было и есть ноль.
L>Это в основном программеры уверены, что пользы от него ноль. А те, кто использует, придерживается иного мнения.
Пока манагеры считают — флаг им, даже интересно. Но когда начинают требовать заистимэйтить количество строк, которые буду написаны при написании некоей фичи, а потом ещё пристают с тем, почему это так много ошибок в подобных эстимэйтах… ИМХО, начинается дурдом Но это уже оффтопик :о)
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Есть обратный пример: CRA>System.Convert CRA>Ведь не сделали так:
CRA>"1".ToInt32()
Именно что сделали, между прочим: класс String реализуетIConvertible Interface, просто реализация сделана явной (implicit interface implementation), для того, что бы не смущать неискушённый программеров обилием методов.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, IB, Вы писали:
S>>сумму ордера конечно же надо хранить. IB>С точки зрения здравного смысла, надо хранить не сумму (алгоритм рассчета которой так же может поменяться), а ставку налога на момент формирования заказа. Впрочем, для финансов, наверное, и сумму тоже имеет смысл хранить.
Не, с точки зрения здравого смысла, хранить нужно именно сумму. Потому что в способе ее подсчета могло измениться всё что угодно — и НДС, и правила округления, и политика скидок, и прочее.
Хранить все компоненты формулы конечно тоже можно, но вот как раз это здравому смыслу противоречит.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, stump, Вы писали:
S>Просто в твоем ответе сквозит такая уверенность в существовании "правильной архитектуры". Слово "правильная" к архитектуре и дизайну софта вообще не применимо (ИМХО).
Совершенно верно. Зато очень часто к архитектуре и дизайну применимо слово "неправильная". Чем и воспользовался AVK.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, adontz, Вы писали:
A>Иван, у тебя получается достаточно странная логика. С одной стороны высокоуровневые рассуждения о связности (я их не опровергаю, даже за) о том как важно разделять и властвовать, с другой стороны, когда речь дошла до практики, вдруг начинаются прыжки в сторону на тему "для эффективной реализации, метод должен быть членом класса". A>У тебя получается некоторый String с методами, которые остались внутри просто потому что их нельзя выдернуть не просадив производительность (абсолютно нелепая выйдет компания) и помойка для всех остальных методов в хелпере. Мне это совсем не видится светлым будущим и, думаю, надо, всё же, чётко разделить теорию и практику. Раскидать методы по N классам достигнув абстрактной крутости и создав абсолютно нелогичный API не представляет мне достойной целью.
Светлое будущее — в том, чтобы abstraction penalty не было. В теории это иногда возможно: например, грамотный инлайнинг методов публичного контракта внутрь хелперного метода позволит применить жесткие оптимизации и добиться производительности, сравнимой с "внутренней" реализацией того же метода.
Тем не менее, мы живем в настоящем, а не в будущем. Поэтому приходится принимать компромиссные решения. В частности, жертвовать maintainbility для получения performance. Прикол в том, что если с самого начала заниматься performance, то потом улучшать maintainability очень дорого — потому, что собственно maintainability и определяет легкость изменения кода. Поэтому IB и AVK настаивают на последовательном проведении в жизнь правильного дизайна, и на обосновании каждого отступления от него в своих решениях.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, MozgC, Вы писали:
MC>Я попросил показать реальный пример про String раз вы утверждаете что он реализован неправильно и эти функции надо было вынести в отдельные классы.
Реальный пример: попробуй реализовать свою версию String по образцу Haskell-овских строк (хранение списка линейных фрагментов вместо одного символьного массива).
Вот тут-то тебя не обрадует то, что от строки требуется что-то большее, чем IEnumerable<Char>.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Светлое будущее — в том, чтобы abstraction penalty не было.
Не было для кого? Для разработчика библиотеки или для пользователя?
S>Тем не менее, мы живем в настоящем, а не в будущем. Поэтому приходится принимать компромиссные решения. В частности, жертвовать maintainbility для получения performance.
Жертвовать maintainbility кода библиотеки или кода используещего библиотеку? Который важнее?
Здравствуйте, adontz, Вы писали:
A>Не было для кого? Для разработчика библиотеки или для пользователя?
Для программы. Ты знаешь, что такое abstraction penalty? Это как раз то, на что ссылаются джедаи, которые говорят: "да я разверну этот цикл на ассемблере так, что все языки без ассемблерных вставок сдохнут от зависти".
Так что твой вопрос не имеет смысла. S>>Тем не менее, мы живем в настоящем, а не в будущем. Поэтому приходится принимать компромиссные решения. В частности, жертвовать maintainbility для получения performance. A>Жертвовать maintainbility кода библиотеки или кода используещего библиотеку? Который важнее?
Приходится жертвовать и там и там. В принципе, конечно же клиентский код важнее — его тупо больше.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.