Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, Jack128, Вы писали:
M>>>В приведенно примере SameMethod(param1, 0) можно реализовать как внешнюю функцию , внешнюю по отношению к классу или модулю. J>>можно. А зачем?? 0>Что бы не плодить дополнительные сущности в контракте. В С#, начиная с версии 3.0, для решения подобных задач есть хороший механизм — extension methods.
что ты имеешь ввиду под словом "сущность" ?? метод?? тогда твоя фраза ничего не объясняет. по сути ты сказал "много методов — плохо , потому что это плохо." либо расшифруй это слово.
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, Jack128, Вы писали:
J>>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
M>Меньше интерфейс, меньше зависимостей и связей.
Да ну...
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, Jack128, Вы писали:
J>>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
M>Меньше баплик методов , меньшн интнрфейс. M>Меньше интерфейс, меньше зависимостей и связей.
вот этот пункт — не очевиден.
пусть есть Class1, имеющий Method1 и Method2. Пусть есть некий Method3, сводящийся к вызову Method1 и Method2. Конкретные доводы, почему я должен реализовывать этот Method3 во внешнем классе есть?
Здравствуйте, Jack128, Вы писали:
0>>Что бы не плодить дополнительные сущности в контракте. В С#, начиная с версии 3.0, для решения подобных задач есть хороший механизм — extension methods. J>что ты имеешь ввиду под словом "сущность" ?? метод?? тогда твоя фраза ничего не объясняет. по сути ты сказал "много методов — плохо , потому что это плохо." либо расшифруй это слово.
Не метод. Я там по ветке ниже уже отписался.
Суть такова: подобные методы не добавляют функционал интерфейсу, а фиксируют некоторые распространенные паттерны употребления методов интерфейса. Т.е. получается, что интрефейс описывает и, собственно, функционал, и наиболее распространенные паттерны вызовов, которые вполне могут быть контекстно-зависимыми. Мой тезис заключается в том, что паттерны вызовов вынести в extesion methods и не замусоривать ими интерфейс.
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, Jack128, Вы писали:
0>>>Что бы не плодить дополнительные сущности в контракте. В С#, начиная с версии 3.0, для решения подобных задач есть хороший механизм — extension methods. J>>что ты имеешь ввиду под словом "сущность" ?? метод?? тогда твоя фраза ничего не объясняет. по сути ты сказал "много методов — плохо , потому что это плохо." либо расшифруй это слово. 0>Не метод. Я там по ветке ниже уже отписался. 0>Суть такова: подобные методы не добавляют функционал интерфейсу, а фиксируют некоторые распространенные паттерны употребления методов интерфейса. Т.е. получается, что интрефейс описывает и, собственно, функционал, и наиболее распространенные паттерны вызовов, которые вполне могут быть контекстно-зависимыми. Мой тезис заключается в том, что паттерны вызовов вынести в extesion methods и не замусоривать ими интерфейс.
"мусорить интерфейс" — это слова. ты можешь конкретный сценарий привести, при котором extension методы будут удобнее просто доп методов у класса?? ну там проще рефакторить или еще что.
Здравствуйте, Jack128, Вы писали:
J>пусть есть Class1, имеющий Method1 и Method2. Пусть есть некий Method3, сводящийся к вызову Method1 и Method2. Конкретные доводы, почему я должен реализовывать этот Method3 во внешнем классе есть?
Если нужен только вызов Method3, то Method1 и Method2 лучше сделать приватными. Тогда это улучшений , интерфейс снановится более узким.
Класс который инкапсулирует сложность работы с другой сущностью, в какомто конкретном применении.
Мотивация к использованию данного шаблона, скорее всего она подойдет для описанного случая.
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, Jack128, Вы писали:
J>>пусть есть Class1, имеющий Method1 и Method2. Пусть есть некий Method3, сводящийся к вызову Method1 и Method2. Конкретные доводы, почему я должен реализовывать этот Method3 во внешнем классе есть?
M>Если нужен только вызов Method3, то Method1 и Method2 лучше сделать приватными. Тогда это улучшений , интерфейс снановится более узким.
M>Если Method3 нужен только для удобства использования, тогда пишется так называемый Фасад. M>http://en.wikipedia.org/wiki/Facade_pattern
M>Класс который инкапсулирует сложность работы с другой сущностью, в какомто конкретном применении. M>Мотивация к использованию данного шаблона, скорее всего она подойдет для описанного случая.
чесно говоря не видел, чтоб фасад скрывал сложность ОДНОГО класса. обычно он скрывает взаимосвязи между множеством классов.
Здравствуйте, Jack128, Вы писали:
J>чесно говоря не видел, чтоб фасад скрывал сложность ОДНОГО класса. обычно он скрывает взаимосвязи между множеством классов.
Так и есть в широкодуступных описаниях. Я же говорю о том что этот шаблон можно и нужно применять в меньших масштабах. Механизмы остаются те же.
Здравствуйте, Jack128, Вы писали:
J>пусть есть Class1, имеющий Method1 и Method2. Пусть есть некий Method3, сводящийся к вызову Method1 и Method2. Конкретные доводы, почему я должен реализовывать этот Method3 во внешнем классе есть?
потому что не бывает классов с Метод1, Метод2, Метод3 и т.д. Бывает класс с конкретными методами, реализующий конкретное поведение.
Если это хранилище с методами Найти и Добавить, то метод НайтиЕслиНетДобавить — вполне логичен (правда может стоит оставить только его...).
А вот метод НайтиИУмножитьНайденноеНа3ЕслиНетСоздатьНовыйКакЛучшийИзПервогоИПоследнего — не логичен, ибо magic заложенный в этот метод никак не относится к хранилищу и ясен только вам, как автору метода. Для него лучше завести отдельный класс MyMagicSequence с таким методом — будет проще сопровождать код.
Ты им пользуешься, он тебя устраивает, но потом ты понимаешь, что твои задачи изменились, и нужно не просто сохранять примитивные типы, а целые графы своих доменных объектов. Ты решаешь написать обобщенную реализацию сериализации графа в файлик и добавляешь новые методы:
И все бы хорошо, но спустя какое-то время ты понимаешь, что INI-файлы это УГ, и нужно использовать что-нибудь более удобное — например, XML или реестр. Что тебе приходится делать? Правильно — переписать весь класс IniFileStorage, а заодно с ним поменять весь клиентский код. А если клиентский код пишешь не только ты, но и многочисленные плагинописатели?
А как можно было бы поступить иначе?
Предположим, у тебя есть класс IniFileStorage, и его методы вынесены в интерфейс:
В итоге получается, что реализации сериализатора вообще по фиг, что ему дают под видом IStorage — ты можешь полностью переделать IStorage, и при этом ни сериализатор, ни клиентский код, который получает уже инициализированный экземпляр сериализатора ничего не заметят.
Стало лучше? Мне кажется, да.
Рассмотрим другой пример. У тебя есть все тот же IniFileStorage. Потом ты понимаешь, что в ряде случаев удобнее использовать генерик-методы для записи и чтения. Ну и добавляешь их в класс.
IniFileStorage
{
void SetValue(string key, object value);
object GetValue(string key);
void SetValue<T>(string key, T value);
T GetValue<T>(string key);
}
У тебя получаются два метода, которые делают ровно то же самое, что и два изначальных, но просто имеют другую форму вызова. Не будем рассматривать крайние примеры, когда ты создаешь распределенное приложение, и из-за изменения контракта придется передеплоить всех клиентов. Хотя такие случае в программировании тоже бывают
Возьмем более банальный пример. У тебя IniFileStorage используется не только напрямую, но и в обобщенном коде — в том же сериализаторе, — которому генерик методы неудобны. И он их никогда не будет вызывать. Другой программист, работающий вместе с тобой, может не знать об этом поведении и также о том, что согласно твоим принципам дизайна SetValue и SetValue<> должны делать абсолютно одно и то же, чтобы все правильно работало. А не знать он может просто потому, что такие паттерны науке неизвестны. И вот он возьмет и добавит какой-нибудь специфичный код в SetValue<>, который согласно новым требованиям должен бы выполняться всегда, но будет работать лишь время от времени, когда вызывается SetValue<>, а не SetValue. А ведь "старые" клиенты вообще не подозревают о существовании SetValue<>.
А послезавтра третий программист добавит еще одну пару методов, которая покажется ему удобной и тоже забьет на все изменения в SetValue<> — или же продублирует их, но не неправильно. Получаем "макароны" на ровном месте и в связи с очень простой задачей. И случай не такой уж и далекий от жизни. А что может быть в реальном проекте, который делает что-то более интеллектуальное, чем запись в ини-файл?
Здравствуйте, Jack128, Вы писали:
J>пусть есть Class1, имеющий Method1 и Method2. Пусть есть некий Method3, сводящийся к вызову Method1 и Method2. Конкретные доводы, почему я должен реализовывать этот Method3 во внешнем классе есть?
Есть — это ухудшает инкапсуляцию, так как Method3 получает доступ к приватному состоянию Class1, что совершенно не нужно для его работы.
Здравствуйте, Jack128, Вы писали:
M>>Меньше баплик методов , меньшн интнрфейс. M>>Меньше интерфейс, меньше зависимостей и связей.
J>вот этот пункт — не очевиден.
J>пусть есть Class1, имеющий Method1 и Method2. Пусть есть некий Method3, сводящийся к вызову Method1 и Method2. Конкретные доводы, почему я должен реализовывать этот Method3 во внешнем классе есть?
Method3 реализует некоторую логику, пользуясь при этом Method1 и Method2. Является ли реализация этой логики частью обязанностей (ответственности) Class1? Строго говоря — нет. Хотя тут возможны исключения — зависит от того, как возникла такая ситуация: в результате рефакторинга или by design. К чему это приводит: изменения Method3 (как части интерфейса) приведут к перекомпиляции всех частей, зависимых от Class1 (и т.д.), хотя эти части не обязательно зависят от Method3. Изменение любых типов-параметров Method3 также приведет к этому, и т.д.
Еще хуже, когда при необходимости изменения реализации Method3 верез Class1 будут протаскиваться дополнительные параметры. В каких-то местах может быть завязка на текущую реализацию Method3, и автоматическое изменение поведения из-за изменения, скажем Method1 может быть нежелательным. Может оказаться что функциональность Method3 уже где-то рализована и реализация Method3 по существу — дублирование кода. И т.п.
В некоторых контекстах подобного рода зависимости могут не иметь особого значения, в некоторых наоборот. По умолчанию — два маленьких (Class1 & Class2) лучше чем один большой (Class1).
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Jack128, Вы писали:
J>>пусть есть Class1, имеющий Method1 и Method2. Пусть есть некий Method3, сводящийся к вызову Method1 и Method2. Конкретные доводы, почему я должен реализовывать этот Method3 во внешнем классе есть? IB>Есть — это ухудшает инкапсуляцию, так как Method3 получает доступ к приватному состоянию Class1, что совершенно не нужно для его работы.
отсюда следует просто вывод: в классе вообще _не должно_ быть методов, которые вызывают только паблик методы этого класса. Ты сам действительно так пишешь???
Здравствуйте, Jack128, Вы писали:
J>отсюда следует просто вывод: в классе вообще _не должно_ быть методов, которые вызывают только паблик методы этого класса. Ты сам действительно так пишешь???
А ты думаешь, что он ответит тебе "нет"? А потом все дружно извинятся и скажут, что эта была просто попытка тебя обмануть, а ты молодец, что не поддался?
ВВ>В итоге получается, что реализации сериализатора вообще по фиг, что ему дают под видом IStorage — ты можешь полностью переделать IStorage, и при этом ни сериализатор, ни клиентский код, который получает уже инициализированный экземпляр сериализатора ничего не заметят. ВВ>Стало лучше? Мне кажется, да.
лудше. Только я не понял, ты выделил отдельный класс сериализатора — только для того чтобы уменьшить кол-во мтеодов в iniStorage'e ?? тогда почему бы не разбить MySerializer на два класс:
MySaver
{
Initialize(IStorage storage);
void Serialize(string key, object graph);
}
MyLoader
{
Initialize(IStorage storage);
object Deserialize(string key, Type objectType);
}
вроде кол-во методов в кадом классе уменьшилось??
ВВ>Возьмем более банальный пример. У тебя IniFileStorage используется не только напрямую, но и в обобщенном коде — в том же сериализаторе, — которому генерик методы неудобны. И он их никогда не будет вызывать. Другой программист, работающий вместе с тобой, может не знать об этом поведении и также о том, что согласно твоим принципам дизайна SetValue и SetValue<> должны делать абсолютно одно и то же, чтобы все правильно работало. А не знать он может просто потому, что такие паттерны науке неизвестны. И вот он возьмет и добавит какой-нибудь специфичный код в SetValue<>, который согласно новым требованиям должен бы выполняться всегда, но будет работать лишь время от времени, когда вызывается SetValue<>, а не SetValue. А ведь "старые" клиенты вообще не подозревают о существовании SetValue<>.
А терь предположим что метод SetValue<> реализован не в INiStorage а в другом классе. И "другой программист возьмет и добавит какой-нибудь специфичный код в SetValue<>, который согласно новым требованиям должен бы выполняться всегда, но будет работать лишь время от времени, когда вызывается SetValue<>, а не SetValue. А ведь "старые" клиенты вообще не подозревают о существовании SetValue<>."
Здравствуйте, Jack128, Вы писали:
J>лудше. Только я не понял, ты выделил отдельный класс сериализатора — только для того чтобы уменьшить кол-во мтеодов в iniStorage'e ?? тогда почему бы не разбить MySerializer на два класс: J>MySaver J>{ J> Initialize(IStorage storage); J> void Serialize(string key, object graph); J>}
J>MyLoader J>{ J> Initialize(IStorage storage); J> object Deserialize(string key, Type objectType); J>} J>вроде кол-во методов в кадом классе уменьшилось??
Ну объясни мне тогда согласно каким принципам ты произвел это разделение и в каких ситуациях оно может помочь.
J>А терь предположим что метод SetValue<> реализован не в INiStorage а в другом классе. И "другой программист возьмет и добавит какой-нибудь специфичный код в SetValue<>, который согласно новым требованиям должен бы выполняться всегда, но будет работать лишь время от времени, когда вызывается SetValue<>, а не SetValue. А ведь "старые" клиенты вообще не подозревают о существовании SetValue<>." J>что изменилось??
Изменилось то, что в первом случае "другой программист" сделает то, что от него и требуется — реализует функционал в классе, за этот функционал отвечающем.
Во втором случае больной на голову программист реализует логику в экстеншине, созданном исключительно ради "удобного доступа" к другому функционалу.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, Jack128, Вы писали: ВВ>Ну объясни мне тогда согласно каким принципам ты произвел это разделение и в каких ситуациях оно может помочь.
я произвел деление по принципу уменьшения кол-ва паблик методов в каждом классе. чем и в каких ситуациях это может помочь я не знаю. я как раз пытаюсь это выяснить в этой теме.
J>>А терь предположим что метод SetValue<> реализован не в INiStorage а в другом классе. И "другой программист возьмет и добавит какой-нибудь специфичный код в SetValue<>, который согласно новым требованиям должен бы выполняться всегда, но будет работать лишь время от времени, когда вызывается SetValue<>, а не SetValue. А ведь "старые" клиенты вообще не подозревают о существовании SetValue<>." J>>что изменилось??
ВВ>Изменилось то, что в первом случае "другой программист" сделает то, что от него и требуется — реализует функционал в классе, за этот функционал отвечающем.
он реализует функционал в соответствующем классе, но не в том методе.
ВВ>Во втором случае больной на голову программист реализует логику в экстеншине, созданном исключительно ради "удобного доступа" к другому функционалу.
и в первом и во втором случае программист косячит.
Здравствуйте, Jack128, Вы писали:
J>я произвел деление по принципу уменьшения кол-ва паблик методов в каждом классе. чем и в каких ситуациях это может помочь я не знаю. я как раз пытаюсь это выяснить в этой теме.
Тебе и пытаются объяснить, что нет такой задачи как "уменьшить количество паблик методов в классе". Паблик методов должно быть ровно столько, сколько нужно. Но не меньше. И не больше.
Зато есть такие прекрасные задачи как сохранение неизменности публичного контракта при изменении функционала или как функциональная декомпозиция, пример которой я выше и показал.
Т.е. я не решал задачу "уменьшить количество паблик методов", у меня ее просто не было. Я показал, к каким проблемам может привести подход, при котором мы не задумываемся над такими умными понятиями как декомпозиция, SRP и пр., а просто лепим все в один класс — ибо если нужно, остальные и так разберутся.
ВВ>>Изменилось то, что в первом случае "другой программист" сделает то, что от него и требуется — реализует функционал в классе, за этот функционал отвечающем. J>он реализует функционал в соответствующем классе, но не в том методе.
Почему не в том? Как узнать, какой тот? По мне так SetValue(object) выглядит вообще несколько deprecated по сравнению с SetValue<T>(T val). Может, первый просто забыли удалить? Или это тупо легаси?
ВВ>>Во втором случае больной на голову программист реализует логику в экстеншине, созданном исключительно ради "удобного доступа" к другому функционалу. J>и в первом и во втором случае программист косячит.
Это точно. Вопрос только в том, какой именно программист косячит в первом и во втором случае.
Здравствуйте, Jack128, Вы писали:
J>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
На эту тему есть хорошая статья у Скотта Майерса. Если кратко, то чем больше методов у класса тем больше кода, который зависит от его внутренностей, т.е. тем хуже инкапсуляция со всеми вытекающими. Впрочем, если в документации к классу указано, что некоторый метод является просто обёрткой для другого метода и все изменения проходят через ревью, проблем может и не будет.
The last good thing written in C was Franz Schubert's Symphony No. 9.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, Jack128, Вы писали:
J>>я произвел деление по принципу уменьшения кол-ва паблик методов в каждом классе. чем и в каких ситуациях это может помочь я не знаю. я как раз пытаюсь это выяснить в этой теме.
ВВ>Тебе и пытаются объяснить, что нет такой задачи как "уменьшить количество паблик методов в классе".
Jack128>> а чем плохо иметь много публичных мемберов?? Вот System.Linq.Enumerable имеет ну просто дохера методов и вроде пока жив-здоров. Control тоже..
Воронков Василий> Товарищ, мне казалось, это вещи достаточно очевидные. Хотите, чтобы вас просветили, создайте тему в философии, у меня сейчас вести дискуссию на эту тему большого желания нет.
ВВ>>>Изменилось то, что в первом случае "другой программист" сделает то, что от него и требуется — реализует функционал в классе, за этот функционал отвечающем. J>>он реализует функционал в соответствующем классе, но не в том методе.
ВВ>Почему не в том? Как узнать, какой тот? По мне так SetValue(object) выглядит вообще несколько deprecated по сравнению с SetValue<T>(T val). Может, первый просто забыли удалить? Или это тупо легаси?
очевидно посмотрев на код метода. И все сразу станет понятно. А если менять метод, не смотря на его текущую реализацию, то косячить наш сферический "другой программист" булет постоянно.