Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Jack128, Вы писали:
J>>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
IT>Это актуально для разработки повторно-используемых компонент, т.к. public методы по сути являются контрактом, который в дальнейшем нельзя менять.
угу, это понятно.
Но например такая тема:
есть метод
public void SameMethod(int param1, int param2)
в 90% случаев он используется SameMethod(param1, 0)
являются ли соображения о публичном контракте поводом НЕ вводить перегрузку SameMethod(int param1) {SameMethod(param1, 0);} ??
Здравствуйте, Jack128, Вы писали:
J>в 90% случаев он используется SameMethod(param1, 0) J>являются ли соображения о публичном контракте поводом НЕ вводить перегрузку SameMethod(int param1) {SameMethod(param1, 0);} ??
Думаю, не являются. В .NET FW это интенсивно используется. Но параметры по-умолчанию, которые появяться в C# 4.0 более правильная практика.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Думаю, не являются. В .NET FW это интенсивно используется. Но параметры по-умолчанию, которые появяться в C# 4.0 более правильная практика.
параметры по умолчанию сами по себе не решают проблему, тока в сосокупности с "именнованными" параметрами(не знаю как он официально называются) в VB есть такая фишка: SameMethod(param2 = 10, param1 = 5)
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, Jack128, Вы писали:
J>>есть метод J>>public void SameMethod(int param1, int param2)
J>>в 90% случаев он используется SameMethod(param1, 0)
M>В приведенно примере SameMethod(param1, 0) можно реализовать как внешнюю функцию , внешнюю по отношению к классу или модулю.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Jack128, Вы писали:
J>>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
L>Потому что чаще всего такая ситуация сведетельствует, что класс берет на себя слишком много. Погуглите по SRP (single responsibility principle).
ну в таком случае — если я вижу в класса — 100 паблик методов — это просто повод посмотреть на него повнимательнее, не более того.
Здравствуйте, Jack128, Вы писали:
IT>>Думаю, не являются. В .NET FW это интенсивно используется. Но параметры по-умолчанию, которые появяться в C# 4.0 более правильная практика.
J>параметры по умолчанию сами по себе не решают проблему, тока в сосокупности с "именнованными" параметрами(не знаю как он официально называются) в VB есть такая фишка: SameMethod(param2 = 10, param1 = 5)
Вот это и будет в 4.0.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Jack128, Вы писали:
L>>Потому что чаще всего такая ситуация сведетельствует, что класс берет на себя слишком много. Погуглите по SRP (single responsibility principle).
J>ну в таком случае — если я вижу в класса — 100 паблик методов — это просто повод посмотреть на него повнимательнее, не более того.
Смотря какую цель ты преследуешь. Если у тебя нет желания писать качественный код, то достаточно просто посмотреть внимательнее, хотя и этого делать не обязательно.
Здравствуйте, Jack128, Вы писали:
J>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
При использовании такого класса придется разбираться в этой куче методов. И если на эти методы повязаться, то при переделке public методов класса придется переделать код, который эти методы использует.
Но здесь есть опасность. Если класс имеет очень сложное внутреннее устройство, и один метод SetValue( ... ), то становиться еще хуже.
Все зависит от конкретного случая. Количество методов доступных пользователю должно быть минимально достаточным для полноценного и эффективного использования функционала, реализуемого классом.
"Много" или "мало" это слишком абстрактно. Наверное, нужно исходить из принципа, что класс должен предоставлять методы, объединяемые в три-четыре семантических единицы, т.е. 3-4 группы методов.
Здравствуйте, Jack128, Вы писали:
J>Здравствуйте, minorlogic, Вы писали:
M>>В приведенно примере SameMethod(param1, 0) можно реализовать как внешнюю функцию , внешнюю по отношению к классу или модулю.
J>можно. А зачем??
Меньше связанность, меньше зависимостей.
Попробуйте ответить на вопрос , почему вообще пишут разные классы модули и т.п. Можно ведь все писать в одном большом модуле всемогутере.
Здравствуйте, Jack128, Вы писали:
J>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
Меньше баплик методов , меньшн интнрфейс.
Меньше интерфейс, меньше зависимостей и связей.
Меньше зависимостей и связей, лучшая декомпозиция.
Лучшая декомпозиция, код гибче к изменениям и потдержке.
Код гибче к изменениям и потдержке, могут заплатить больше денежек
Do make only the longest overload virtual (Overridable in Visual Basic) if extensibility is required. Shorter overloads should simply call through to a longer overload.
...
Do allow null (Nothing in Visual Basic) to be passed for optional arguments. If a method takes optional arguments that are reference types, allow null to be passed to indicate that the default value should be used. This avoids the problem of having to check for null before calling a member.
Так что правильно приготовленные overloads — не более чем сахар для вызова основного мембера и контракт не портят.
Здравствуйте, Jack128, Вы писали:
M>>В приведенно примере SameMethod(param1, 0) можно реализовать как внешнюю функцию , внешнюю по отношению к классу или модулю. J>можно. А зачем??
Что бы не плодить дополнительные сущности в контракте. В С#, начиная с версии 3.0, для решения подобных задач есть хороший механизм — extension methods.
Здравствуйте, 0x7be, Вы писали:
0>Что бы не плодить дополнительные сущности в контракте. В С#, начиная с версии 3.0, для решения подобных задач есть хороший механизм — extension methods.
Ребят, а никому не кажется странным решение в стиле структурного программирования в эпоху развитого ООП? Перед extension methods слегка другие задачи ставились.
— They are not discoverable. You can't just look at intellisense or the class documentation to discover its capabilities. You have to have foreknowledge that the functionality you want exists and what namespace to find it in.
— The are fragile. Extension methods introduce a new version of the Fragile Base Class problem, where an instance method introduced in a later version of a class will silently and transparently replace the extension method. For some reason, no steps were taken to mitigate this problem in the design of extension methods.
Здравствуйте, Sinix, Вы писали:
0>>Что бы не плодить дополнительные сущности в контракте. В С#, начиная с версии 3.0, для решения подобных задач есть хороший механизм — extension methods. S>Ребят, а никому не кажется странным решение в стиле структурного программирования в эпоху развитого ООП? Перед extension methods слегка другие задачи ставились.
Не совсем. В данном случае extension methods не добавляют функциональности классу/интерфейсу, который расширяют, а всего ли реализую некоторые часто употребляемые паттерны вызова. При этом эти паттерны могут быть специфичны для конкретного случая использования и не являются каким-то неотъемлемым свойством потребляемого интерфейса.
S>- They are not discoverable. You can't just look at intellisense or the class documentation to discover its capabilities. You have to have foreknowledge that the functionality you want exists and what namespace to find it in.
В эпоху развитого intellisense это не так
S>- The are fragile. Extension methods introduce a new version of the Fragile Base Class problem, where an instance method introduced in a later version of a class will silently and transparently replace the extension method. For some reason, no steps were taken to mitigate this problem in the design of extension methods. S>[/q]
Тут не поспоришь.
"У всех свои недостатки" (с)
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, Sinix, Вы писали:
0>Не совсем. В данном случае extension methods не добавляют функциональности классу/интерфейсу, который расширяют, а всего ли реализую некоторые часто употребляемые паттерны вызова. При этом эти паттерны могут быть специфичны для конкретного случая использования и не являются каким-то неотъемлемым свойством потребляемого интерфейса.
Ну вообще-то расширения изначально планировались контекстными. Грубо говоря using System.Net — и у строки появляются фичи, нужные для работы с сетями (например "localhost".Ping()). Пруфлинк не найду. В мейнстриме реализация ограничилась линком (и правильно имхо).
0>В эпоху развитого intellisense это не так
Развитому интеллисенсу надо ещё скормить пачку юзингов. Ну и не все изучают библиотеки через автодополнение. Тот же рефлектор о наличии расширений умалчивает.
0>"У всех свои недостатки" (с)
+1
Здравствуйте, 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). Может, первый просто забыли удалить? Или это тупо легаси?
очевидно посмотрев на код метода. И все сразу станет понятно. А если менять метод, не смотря на его текущую реализацию, то косячить наш сферический "другой программист" булет постоянно.
Может, тогда всю тамошнюю тему процитируете, раз уж хочется ссылками перебрасываться? Речь шла о том, почему плохо иметь два паблик метода, делающих абсолютно одно и то же и отличающихся лишь формой вызова.
И да, вы знаете, сто методов в публичном контракте это плохо само себе, потому что у класса как бы обычно есть свое узкое предназначение. В противном случае вообще неясно, зачем вы программирует на ОО-языке.
Может, стоит, кстати, издалека начать? Давайте вы спросите, зачем нужны классы если все можно лепить внутри одного static class Program.
J>очевидно посмотрев на код метода. И все сразу станет понятно. А если менять метод, не смотря на его текущую реализацию, то косячить наш сферический "другой программист" булет постоянно.
Ну в вашей вселенной для любых изменений, видимо, придется изучать весь написанный до этого код. К счастью, обычно это не так.
Здравствуйте, Jack128, Вы писали:
J>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
Для начала следует определиться о каких классах идет речь: о библиотеках или о класса бизнес-логики. Этот момент очень важен, т.к. акценты в обоих случаях существенно отличаются.
Если речь идет о библиотеках, то ключевой вопрос, который задает себе архитектор следующий: как мне сделать так, чтобы этот класс (или библиотеку целиком) вообще использовали, а не принялись выдумывать собственный велосипед? В этом случае предъявляются другие требования к качеству и надежности, команда разработчиков проводит всяческие usability studies для определения и уменьшения "порога вхождения" (сколько нужно времени пользователю, чтобы он смог начать успешно пользоваться библиотекой). В случае с библиотечным классом не целесообразно разбивать класс на несколько, с целью упрощения последующего сопровождения; главным в этом случае был и остается пользователь библиотеки, а не ее разработчик. Для проектирования библиотек требует больше опыта и меньше фантазиии, чем более распространенные решения будут применяться, тем больше вероятность, что они будут удобны и понятны пользователю
(в Framework Design Guidelines достаточно много внимания уделено вопросу разработки библиотек и большая часть проблем, с которыми сталкиваются разработчики библиотек, поэтому если вопрос касается библиотек — то вам туда)
Итак, далее я буду рассчитывать, что речь идет о сущностях, предназначенных для решения некоторых бизнес-задач некоторого приложения и что эти сущности не предназначены для повторного использования в тысячами или десятками тысяч клиентов.
Ключевые проблемы, с которыми сталкиваются проектировщики (причем не только программного обеспечения) были замечены более 40 лет назад (возможно раньше...). Одними из первых в области разработки ПО эти принципы озвучили Эд Йордон и Ларри Константайн в 1979 году в своей книге “Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design” основываясь на понятиях, приведенных в книге Кристофера Алексендера “Notes On The Synthesis Of Form”.
По мнению Александера основной задачей при декомпозиции системы является осуществление следующих двух условий:
— максимизация связей внутри компонентов (высокое сцепление, high cohesion) и
— минимизация связей между компонентами (низкая связанность, low coupling).
Кроме этого, позднее был определен ряд принципов и критериев, качества проектной модели.
В частности существует ряд принципов проектирования, получивших название SOLID:
— S — SRP — Single responsibility principle
— O — OCP — Open/closed principle
— L — LSP — Liskov substitution principle
— I — ISP — Interface segregation principle
— D — DIP — Dependency inversion principle
А также существует ряд критериев для определения качества абстракции: зацепление; связность; достаточность; полнота; примитивность (подробнее см. книгу Гради Буча "ООА и ООП").
Несмотря на наличие достаточного количества формальных принципов, все в конечном итоге зависит от опыта и навыков архитектора, и зачастую интуиции и опыта бывает достаточно, чтобы судить о качестве тех или иных архитектурных решений. При этом, нельзя говорить, что количество открытых функций в этом вопросе играет решающую роль, любой "раздутый" класс тяжело анализировать, будь в нем 50 открытых функций, 50 защищенных или 100 закрытых.
Предположим есть класс, который содержит 2 открытые функции, семантически никак не связанные друг с другом, содержит 27 защищенных функции и 53 закрытых, а также содержит 20 членов данных. Этот класс отвечает критерию небольшого открытого интерфейса, но при этом обладает слабым сцеплением и высокой связанностью, и написан через одно место, поэтому разобрать для чего он нужен и нужен ли вообще, представляется весьма сложным...
Не нужно быть буквальным и всегда четко следовать определенным принципам... В конечном итоге основная задача разработчиков — это создать готовый продукт к определенному сроку с определенным функционалом и качеством. Хороший дизайн не должен быть самоцелью, это всего лишь средство упростить решение текущих задач и уменьшить стоимость последующих доработок или сопровождения. Кроме того, многие разработчики склонны считать, что все, что сделано не ими является ошибочным, поэтому как бы вы не стремились, но даже самый хороший дизайн, будет не понятен тем, кто не захочет его понимать.
Здравствуйте, Jack128, Вы писали:
J>отсюда следует просто вывод: в классе вообще _не должно_ быть методов, которые вызывают только паблик методы этого класса.
Совершенно верно.
J> Ты сам действительно так пишешь???
Да, действительно так пишу. Код от этого значительно чище и понятнее — попробуй.
ВВ>Может, тогда всю тамошнюю тему процитируете, раз уж хочется ссылками перебрасываться? Речь шла о том, почему плохо иметь два паблик метода, делающих абсолютно одно и то же и отличающихся лишь формой вызова.
авторитетный товарищ согласился, что это вполне допустимо. как тебя понимать, Саид??
А вообще — было бы хорошо иметь запрет на уровне синтаксиса — у класса не более 99 методов. Было бы круто
ВВ>И да, вы знаете, сто методов в публичном контракте это плохо само себе, потому что у класса как бы обычно есть свое узкое предназначение. В противном случае вообще неясно, зачем вы программирует на ОО-языке.
ВВ>Может, стоит, кстати, издалека начать? Давайте вы спросите, зачем нужны классы если все можно лепить внутри одного static class Program.
J>>очевидно посмотрев на код метода. И все сразу станет понятно. А если менять метод, не смотря на его текущую реализацию, то косячить наш сферический "другой программист" булет постоянно.
ВВ>Ну в вашей вселенной для любых изменений, видимо, придется изучать весь написанный до этого код. К счастью, обычно это не так.
Здравствуйте, minorlogic, Вы писали:
J>>есть метод J>>public void SameMethod(int param1, int param2)
J>>в 90% случаев он используется SameMethod(param1, 0)
M>В приведенно примере SameMethod(param1, 0) можно реализовать как внешнюю функцию , внешнюю по отношению к классу или модулю.
в данном случае эта функция не нужна.
Если параметров не два а поболе, лучше тогда параметры вынести в отдельный класс и там поперегружать конструкторы.
Здравствуйте, Jack128, Вы писали:
J>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
Само по себе число public методов (и классов) не о многом говорит, и минимизировать их число не самоцель.
У Enumerable<T> много public методов, а приватных могло бы вобще не быть. И они практически независимы, связаны только по смыслу.
Хорошо не минимальное число public, а когда мухи от котлет хорошо отделены.
Если делается интерфейс над подсистемой в виде сложного(и внутри сильно повязанного) графа, не поддающегося разделению. Т.е. публик члены образуют неделимую систему, и у нее сложная реализация, тогда однозначно выметать оттуда все хелперы — маленькие функции которые можно сделать снаружи успользуя публичный интерфейс.
А если класс — это хелпер какой-то, то пихать можно все что упростит частое использование.
Хелперы не нужно совсем удалять просто они должны быть в соответствующих местах.
А для промежуточных случаев, однозначно ничего не скажешь, дело вкуса.
Дурные примеры минимизации public тоже есть:
Взять например DirectX 10. То что там сделали в некоторых местах это просто ужасно, ни в какие рамки не лезет. То что он низкоуровневый не оправдание . Единственные оправдания — то что он в виде COM интерфейса и очень публичный.
Там применен стиль — "флаговое программирование". Это делается так, собирается несколько классов с совершенно разным функционалом, сваливается все в один (например, буфер). И при помощи флажков, при создании объекта указывается какой функционал создать. В дальнейшем, какие функции в этом классе можно вызывать и с какими комбинациями флажков, зависит от того с какими флажками создали. При недопустимых комбинациях либо ошибка "invalid parametr passed" , либо вызов игнорируется. Причем допустимых комбинаций гораздо меньше 1% от общего числа.
Видимо такой вид API не из-за кривых рук(хотя это тоже возможно), а из-за попыток уменьшить число COM интерфейсов, флажки ведь в COM гораздо дешевле.
То что в документации наверно процентов 15% текста, это оговорки — "Если с таким то флажком раньше такую-то функцию вызывал, то теперь эту функцию с таким флажком уже не вызовешь." Или — "Если этот флажок указан, то он отменяет все остальные".
Это еще не значит что они описали все грабли, их гораздо больше.
Здравствуйте, Jack128, Вы писали:
J>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
1. Меньше знаешь, лучше спишь
2. Windows — поставил и работай. Unix — вы можете сконфигурировать всё!!! И вы будете конфигурировать всё
3. ...
А фиг с ним с примерами
Удобство — это основа. Публичных методов должно быть ровно столько, чтобы можно выполнить работу, и ни на один метод больше, чтобы не смущать нежные умы начинающих и малоопытных. Это как в той истории про Дзен, когда учитель говорит, что главное — это баланс, но чтоб прийти к этой истине тебе сначала скажут, что "плохо иметь много паблик методов?", потом ты поймёшь, что на самом деле иногда можно, затем, что на самом деле всё равно, и в итоге, что самое главное — это соблюдать баланс между тем, что нужно и тем что удобно.
J>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
На самом деле всё очень просто.
Паблик методы — это внешний интерфейс: его надо согласовывать, за него надо отвечать, его надо описывать, его надо поддерживать.
Здравствуйте, VGn, Вы писали:
J>>Один авторитетный, судя по рейтингу, rsdn'овиц заявил сабж. Кто ниту может объяснить почему так?
VGn>На самом деле всё очень просто. VGn>Паблик методы — это внешний интерфейс: его надо согласовывать, за него надо отвечать, его надо описывать, его надо поддерживать.
Это не отвечает на вопрос почему это плохо, т.к. от того, что эти методы будут разбиты на группы и разбросаны по нескольким классам, "совокупный" публичный интерфей от этого не уменьшится.
L>Это не отвечает на вопрос почему это плохо, т.к. от того, что эти методы будут разбиты на группы и разбросаны по нескольким классам, "совокупный" публичный интерфей от этого не уменьшится.
Про количество классов вопроса не было. Был вопрос про количество методов.
Если, перемещаясь в другой класс, методы остаются публичными, при чём "много паблик методов"? Можете их вообще в веб-интерфейс перевести. Меньше их от этого не станет.
Здравствуйте, VGn, Вы писали:
L>>Это не отвечает на вопрос почему это плохо, т.к. от того, что эти методы будут разбиты на группы и разбросаны по нескольким классам, "совокупный" публичный интерфей от этого не уменьшится.
VGn>Про количество классов вопроса не было. Был вопрос про количество методов. VGn>Если, перемещаясь в другой класс, методы остаются публичными, при чём "много паблик методов"? Можете их вообще в веб-интерфейс перевести. Меньше их от этого не станет.
ну и тогда нахрен их вообще перемещать куда то? плодить кучу классов просто так??
Здравствуйте, Jack128, Вы писали:
J>ну и тогда нахрен их вообще перемещать куда то? плодить кучу классов просто так??
Да и зачем вообще классы нужны? WinAPI рулит!
Здравствуйте, Lloyd, Вы писали:
L>Это не отвечает на вопрос почему это плохо, т.к. от того, что эти методы будут разбиты на группы и разбросаны по нескольким классам, "совокупный" публичный интерфей от этого не уменьшится.
Во-первых не факт.
Во-вторых с большой вероятностью уменьшится объем писанины — не надо будет объяснять, чем конкретно вот этот метод отличается от вон того, такого же, но без одного параметра.
Ну и в-третьих если в разных местах кода требуется вызов разных методов, то описать это с помощью разных интерфейсов — часто бывает неплохой идеей само по себе, безотносительно размера "совокупного интерфея".
Здравствуйте, Jack128, Вы писали:
J>ну и тогда нахрен их вообще перемещать куда то? плодить кучу классов просто так??
потому что с классами Список, Стек, Дерево, Словарь и т.п. гораздо удобнее работать чем с классом Хранилище_Эмулирующее_Любое_Поведение с кучей флажков и методов которые нужно дернуть в правильном порядке для превращения Хранилища в Список, Стек, Дерево, Словарь и т.п.
Здравствуйте, jhfrek, Вы писали:
J>Здравствуйте, Jack128, Вы писали:
J>>ну и тогда нахрен их вообще перемещать куда то? плодить кучу классов просто так??
J>потому что с классами Список, Стек, Дерево, Словарь и т.п. гораздо удобнее работать чем с классом Хранилище_Эмулирующее_Любое_Поведение с кучей флажков и методов которые нужно дернуть в правильном порядке для превращения Хранилища в Список, Стек, Дерево, Словарь и т.п.
понимаешь, бывает я ничего не имеею против "single responsibility principle" . Тока нарушение этого приниципа — не единственная причина, которая приводит к большому кол-ву методов. Что делать в таких случаях??
Здравствуйте, Jack128, Вы писали:
J>понимаешь, бывает я ничего не имеею против "single responsibility principle" . Тока нарушение этого приниципа — не единственная причина, которая приводит к большому кол-ву методов. Что делать в таких случаях??
Здравствуйте, jhfrek, Вы писали:
J>Здравствуйте, Jack128, Вы писали:
J>>понимаешь, бывает я ничего не имеею против "single responsibility principle" . Тока нарушение этого приниципа — не единственная причина, которая приводит к большому кол-ву методов. Что делать в таких случаях??
J>нужен пример "не единственной причины"
Здравствуйте, Jack128, Вы писали:
J>>>понимаешь, бывает я ничего не имеею против "single responsibility principle" . Тока нарушение этого приниципа — не единственная причина, которая приводит к большому кол-ву методов. Что делать в таких случаях??
J>>нужен пример "не единственной причины"
J>пример класса: Control .
чего Control? — графический али реальный ака рубильник
Как вариант: методы Enable\Disable, Perform и свойство Enabled, хотя может быть достаточно одного Perform