Re[5]: Single Responsibility Principle
От: Qbit86 Кипр
Дата: 08.12.10 20:14
Оценка: +1
Здравствуйте, Tom, Вы писали:

Tom>Ерунда это несусветная. Важность либо что то ещё никак не влияет на решение выносить что то в отдельный метод или класс или нет. Единственное что на это влияет — это Single Responsibility Principle. Если метод имеет несколько responsibility то его надо разбивать не учитывая сколько раз и откуда будут вызываться новые методы.


Разбивать — надо. Выносить — не факт. Ты незаметно подменил одно понятие другим, которое показалось тебе синонимичным.

Tom>Принципы хорошего дизайна не надо формулировать, их уже сформулировали. Остаётся им только следовать.


Да, и помимо SRP есть принцип минимизации области видимости.
Глаза у меня добрые, но рубашка — смирительная!
Re[5]: Лямбда-выражения: зачем и почему
От: WolfHound  
Дата: 08.12.10 21:12
Оценка: +2
Здравствуйте, Tom, Вы писали:

Tom>>>То есть если метод будет используется только один раз и вызывается только из одного места — то не имеет смысла заводить такой метод

S>>Как правило — да.
Tom>Только у тех кто НЕ пишет юнит тестов и НЕ соблюдает single responsibility pronciple, но как правило да, на пост советском пространстве эти, как и другие принципы SOLID, не соблюдаются что приводит к очень низкому качеству кода
Давай рассмотрим конкретный пример.
Есть вот такая функция:
  public module Util//В моделу все функции статические.
  {
    public GraphWalk[Key, Value]( start   : IEnumerable[Key]
                                , calc    : Key * (Key -> Value) -> Value
                                , onCycle : Key -> Value)
                                : Map[Key, Value]
    {
      mutable result = Map();
      def visited = Hashtable();
      def getValue(key)
      {
        if (result.Contains(key))
          result.Get(key);
        else
        {
          def value = if (visited.ContainsKey(key))
            onCycle(key);
          else
          {
            visited.Add(key, 0);
            def value = calc(key, getValue);
            visited.Remove(key);
            value;
          }
          result = result.Replace(key, value);
          value;
        }
      }
      foreach (key in start)
        _ = getValue(key);
      result;
    }

Ее задача пройтись по графу неизвестной природы и посчитать что-то для каждой вершины.
На вход подается список вершин графа с которых надо начать обход.
calc функция которая вычисляет значение для очередной вершины графа. Ей на вход передается вершина и функция которая возвращает вычисленное значение для другой вершины.
onCycle вызывается если алгоритм нашол цикл в графе.
И то как эта функция используется:
    public static CalcRulesWeights(grammar : Grammar) : Map[NameRef, option[int]]
    {
      def calcRuleWeight(name, getWeight)
      {
        def calc(rule)
        {
          def add(_, _)
          {
            | (Some(weight1), Some(weight2)) => Some(weight1 + weight2);
            | _ => None();
          }
          def weight = match (rule : Rule)
          {
            | Call(name)               => getWeight(name);
            | Choice(rules)
            | Sequence(rules)          => rules.Fold(Some(0), (rule, weight) => add(weight, calc(rule)));
            | Scope(_, rule)
            | Capture(_, rule)
            | RepeatMin(_, rule)
            | RepeatMinMax(_, _, rule)
            | Not(rule)
            | And(rule)                => calc(rule);
            | Chars | Fsm | Cut        => Some(0)
          }
          add(weight, Some(1));
        }
        match (grammar.GetRule(name).Rule)
        {
          | Some(rule) => calc(rule)
          | _          => Some(0)
        }
      }
      Util.GraphWalk(grammar.Names, calcRuleWeight, _ => None());
    }

А вот теперь покажи какую из вложенных функций можно сделать отдельным методом и для чего это может понадобиться?
Так же не забывай что там полно замыканий. Так что тебе придется еще и классы нагородить...

Код на языке немерле. При написании этого кода ни один тип не пострадал. Они все были выведены компилятором.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re: Лямбда-выражения: зачем и почему
От: Аноним  
Дата: 08.12.10 21:30
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>Здравствуйте!

J_K>Объясните пожалуйста, на пальцах, зачем использовать лямбда-выражения, с примерами настоящего использования.
J_K>Который день пытаюсь разобраться, но смысл ускользает.
J_K>Расщепление кода на методы — это понятно, для удобства чтения, отладки и т.д. С лямбда-выражениями как будто наоборот — мы в коде указываем методы, которые должна выполнить наша функция. От этого код становится ну совершенно нечитаемым, но может, у меня плохие примеры. Дайте тогда хорошие.
J_K>Спасибо!

Лямбда-выражение это примерно как китайский язык — в одной закорючке много смысла. И понимают эти закорючки те кто этим языком владеет.
Но по простоте изучения, в отличии от китайского сравним с изучением операций + и -.
Почему бы его не использовать на практике когда это удобно.

Вот пример, не самый лучший, но то что вертится в данный момент
///  удалить все элементы у которых последнее обновление было более 
// 10 минут назад
myList.Remove( c => ( DateTime.Now - c.LastUpdate ).Minutes > 10 );
Re[6]: Single Responsibility Principle
От: Tom Россия http://www.RSDN.ru
Дата: 09.12.10 10:14
Оценка:
Q>Разбивать — надо. Выносить — не факт. Ты незаметно подменил одно понятие другим, которое показалось тебе синонимичным.
Можно пояснить? Лично я имел ввиду что функцию имеющую только одну responsibility и оформленную именно в виде функции проще тестировать, поддерживать и читать.

Tom>>Принципы хорошего дизайна не надо формулировать, их уже сформулировали. Остаётся им только следовать.

Q>Да, и помимо SRP есть принцип минимизации области видимости.
SRP его никак не нарушает. Выносить можно и нужно в приватные методы.
Народная мудрось
всем все никому ничего(с).
Re[6]: Принцип локальности
От: Tom Россия http://www.RSDN.ru
Дата: 09.12.10 10:17
Оценка:
Q>Вынесение одноразового метода нарушает принцип локальности. Есть риск, что кто-то из команды тоже станет использовать твой метод, и в дальнейшем ты не сможешь его изменить, не затронув чужой код. Загрязняет пространство имён, вывод intellisense.
Если кто то другой начнёт использовать этот метод — это означает что код написан правильно и его можно повторно использовать. Если ты всунешь всё в один метод и твои коллеги не смогут зареюзить твой метод — то они напишут тоже самое, вот это действительно должно пугать При этом как я уже сказал принцип локальности SRP не нарушает, выносить можно и нужно в приватные методы.
Народная мудрось
всем все никому ничего(с).
Re[6]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 09.12.10 10:19
Оценка: :)
WH>А вот теперь покажи какую из вложенных функций можно сделать отдельным методом и для чего это может понадобиться?
WH>Так же не забывай что там полно замыканий. Так что тебе придется еще и классы нагородить...
Без проблем покажу, ты только тесты к своим локальным функциям опубликуй
Народная мудрось
всем все никому ничего(с).
Re[7]: Лямбда-выражения: зачем и почему
От: _FRED_ Черногория
Дата: 09.12.10 10:29
Оценка:
Здравствуйте, Tom, Вы писали:

WH>>А вот теперь покажи какую из вложенных функций можно сделать отдельным методом и для чего это может понадобиться?

WH>>Так же не забывай что там полно замыканий. Так что тебе придется еще и классы нагородить...
Tom>Без проблем покажу, ты только тесты к своим локальным функциям опубликуй

А зачем им тесты, если будут тесты на именованные методы?
Help will always be given at Hogwarts to those who ask for it.
Re[7]: Лямбда-выражения: зачем и почему
От: hardcase Пират http://nemerle.org
Дата: 09.12.10 11:30
Оценка:
Здравствуйте, Tom, Вы писали:

WH>>А вот теперь покажи какую из вложенных функций можно сделать отдельным методом и для чего это может понадобиться?

WH>>Так же не забывай что там полно замыканий. Так что тебе придется еще и классы нагородить...
Tom>Без проблем покажу, ты только тесты к своим локальным функциям опубликуй

Т.е. создание дополнительных классов (и придумывание им имен) для решения чисто алгоритмических задач вообще никак не пугает?
/* иЗвиНите зА неРовнЫй поЧерК */
Re[7]: Тестирование приватных методов
От: Qbit86 Кипр
Дата: 09.12.10 12:05
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Без проблем покажу, ты только тесты к своим локальным функциям опубликуй


Многие вообще считают добавление в тесты приватных методов порочной практикой — а ты говоришь про тестирование одноразовых локальных методов. Которые к тому же могут замыкать контекст, и насильно извлечь их из метода можно только в виде класса с состоянием.

Кроме того, тестирование тривиальных лямбд во многих случаях будет фикцией и тавтологией. Потому что, фактически, придётся тестировать библиотечные вызовы вроде сортировки или сравнения, причём с помощью самих же этих методов.
Глаза у меня добрые, но рубашка — смирительная!
Re[7]: Лямбда-выражения: зачем и почему
От: WolfHound  
Дата: 09.12.10 12:12
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Без проблем покажу, ты только тесты к своим локальным функциям опубликуй

Сразу после того как ты опубликуешь тесты на каждый иф и цикл в твоей программе.
Ну так как? Надешь что там можно повторно использовать?
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[7]: Single Responsibility Principle
От: WolfHound  
Дата: 09.12.10 12:26
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Можно пояснить? Лично я имел ввиду что функцию имеющую только одну responsibility и оформленную именно в виде функции проще тестировать, поддерживать и читать.

Давай вернемся к моему примеру
Автор: WolfHound
Дата: 09.12.10
.
Там есть такая лямбда:
(rule, weight) => add(weight, calc(rule))

Вот скажи мне как ты собрался ее тестировать без функции calc (внутри которой описана эта лямбда) которая замкнута на аргумент getWeight функции calcRuleWeight которая в свою очередь замкнута на аргумент grammar функции CalcRulesWeights?
Также нужно понимать что использование calcRuleWeight кроме как совместно с GraphWalk которому третьим аргементом передается лямбда _ => None() не имеет никакого смысла.
И еще вопрос: Как вынесение _ => None() в отдельный метод улучшит читаемость, поддерживаемость и самое главное что тут можно протестировать?
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[7]: Принцип локальности
От: Qbit86 Кипр
Дата: 09.12.10 12:42
Оценка: +1
Здравствуйте, Tom, Вы писали:

Q>>Есть риск, что кто-то из команды тоже станет использовать твой метод, и в дальнейшем ты не сможешь его изменить, не затронув чужой код.


Tom>Если кто то другой начнёт использовать этот метод...


...То ты теряешь власть над кодом. Ты больше не можешь свободно его менять под нужды задачи — потому что кто-то другой делает свои предположения о его работе, и эти предположения, возможно, будут нарушены.

Tom>...это означает что код написан правильно...


:) Ах, если бы...

Tom>...и его можно повторно использовать.


Это недопустимо, чтобы вопрос повторного использования кода решался произвольно — удался метод или не удался. Это нарушает принципы построения библиотек. На эту тему был хороший доклад на PDC 2008 от авторов «Framework Design Guidelines».

Tom>При этом как я уже сказал принцип локальности SRP не нарушает,


Способ, которым ты предлагаешь реализовать SRP — нарушает.

Tom>выносить можно и нужно в приватные методы.


Приватные методы не так приватны, как локальные функции. Вынесешь метод на уровень класса, и перестанешь контролировать всех коллеров и контекст вызова. Разве что ты работаешь в команде из одного человека, да и то.
Глаза у меня добрые, но рубашка — смирительная!
Re[7]: SRP ≠ «вынесение в приватные методы»
От: Qbit86 Кипр
Дата: 09.12.10 13:01
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Можно пояснить? Лично я имел ввиду что функцию имеющую только одну responsibility и оформленную именно в виде функции проще тестировать, поддерживать и читать.


Не обязательно выносить её на уровень класса. Ей желательно оставаться локальной функцией; если таковые не поддерживаются языком, то, быть может, именованной (при необходимости) лямбдой. В F# проще и единообразнее, просто дерево let-binding'ов. Отлично читается, отлично видна область видимости. А если вынести, то фактическая область видимости резко возрастёт, а актуальная область видимости будет очень плохо просматриваться. Следовательно, нельзя быстро делать оценки того, какие последствия повлечёт за собой та или иная модификация кода (который живёт, непрерывно меняется и всё такое).

В C# — да, иногда лучше вынести в отдельный метод, но это не от хорошей жизни, и это не является необходимым условием соблюдения SRP.

Тестирование приватных методов — это неоднозначная тема, надо выносить в отдельную ветку или гуглить по форумам существующие.

Q>>Да, и помимо SRP есть принцип минимизации области видимости.

Tom>SRP его никак не нарушает. Выносить можно и нужно в приватные методы.

SRP — не нарушает. (И я это уже говорил.) Он вообще ортогонален минимизации области видимости. А вот вынесение методов наружу — нарушает.

Ещё раз, SRP ≠ «вынесение в приватные методы».
Глаза у меня добрые, но рубашка — смирительная!
Re[8]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 09.12.10 13:26
Оценка: -3
WH>Ну так как? Надешь что там можно повторно использовать?
В таком виде как ты написал? Т.е. ты написал решение которое не поддаётся юнит тестированию и просишь меня написать тесты?
Народная мудрось
всем все никому ничего(с).
Re[8]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 09.12.10 13:35
Оценка:
H>Т.е. создание дополнительных классов (и придумывание им имен) для решения чисто алгоритмических задач вообще никак не пугает?
Меня пугает нарушение SRP и проблемы при написании тестов.
Народная мудрось
всем все никому ничего(с).
Re[8]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 09.12.10 13:36
Оценка:
_FR>А зачем им тесты, если будут тесты на именованные методы?
Если у тебя в твоей именованной функции используется с 10-ок лямбд/анонимных методов итп то попробуй написать юнит тесты...
Народная мудрось
всем все никому ничего(с).
Re[9]: Тестирование лямбд
От: Qbit86 Кипр
Дата: 09.12.10 14:24
Оценка: +1
Здравствуйте, Tom, Вы писали:

_FR>>А зачем им тесты, если будут тесты на именованные методы?

Tom>Если у тебя в твоей именованной функции используется с 10-ок лямбд/анонимных методов итп то попробуй написать юнит тесты...

Если ты собираешься тестировать детали реализации (каковыми являются локальные функции), то это НЕ юнит-тесты. Это даже не бесполезные тесты — это вредные тесты. «Если в тестах будут приватные методы, этот класс будет трудно изменять.»

Автоматические тесты особенно полезны при рефакторинге. Не меняя сигнатур тестируемых методов, не меняя по большему счёту поведения, внешних проявлений (кроме исправления ошибок) или по крайней мере, ожиданий (expectations), ты (почти) эквивалентными преобразованиями оптимизируешь реализацию, фиксишь баги, etc. Тесты при этом должны быть устойчивыми, то есть не должны переставать компилироваться, и вообще оставаться адекватными. Устойчивость тестов — один из основных критериев их качества, и качества тестируемого кода. Написание килострок хрупких, ломких тестов, заглядывающих «внутрь» SUT'а — это не TDD, это имитация бурной деятельности перед начальством, профанация и саботаж рабочего процесса. Не говоря уже о том, что тестировать приватные методы чисто технически сложно, нужны InternalsVisibleTo, приватные ключи (даже не их токены), etc.

Ещё раз: поищи про тестирование приватных методов темы на форуме, а также в блогах Фаулера, Спольски, etc.
Глаза у меня добрые, но рубашка — смирительная!
Re[9]: Лямбда-выражения: зачем и почему
От: Ziaw Россия  
Дата: 09.12.10 14:43
Оценка: 1 (1) +1
Здравствуйте, Tom, Вы писали:

WH>>Ну так как? Надешь что там можно повторно использовать?

Tom>В таком виде как ты написал? Т.е. ты написал решение которое не поддаётся юнит тестированию и просишь меня написать тесты?

Да не надо тестировать каждую строчку кода, например Gaperton хорошо рекламировал принцип aufragistik (наверняка я напутал немецкой с орфографией), суть его в том, чтобы давать подчиненным не детальную инструкцию по выполнению, а требования к результату. Так и с юнит тестами, мы тестируем требования к конечному результату работы класса, каким образом он их достигнет, нас не должно волновать (количество и качество приватных методов для теста не важны).

Написав тест, мы зафиксировали требования к юниту, в дальнейшем мы можем на него положиться. При любых его внутренних рефакторингах нас не будет волновать изменение тестов. Вообще, если нам при локальном рефакторинге пришлось менять тесты это ошибка писателя тестов, ибо требования остального кода к классу не изменились.

Более того, если мы вынуждены писать тесты к приватным методам, мы уже нарушили SRP — наш класс перестал быть SR, ибо приватные методы получили свою R.
Re[9]: Лямбда-выражения: зачем и почему
От: WolfHound  
Дата: 09.12.10 14:51
Оценка:
Здравствуйте, Tom, Вы писали:

WH>>Ну так как? Надешь что там можно повторно использовать?

Tom>В таком виде как ты написал? Т.е. ты написал решение которое не поддаётся юнит тестированию и просишь меня написать тесты?
Я тебя спрашиваю что тут можно повторно использовать?
Также меня интересует тестируешь ли ты каждый цикл в своей программе? Или всетки до такого маразма не доходишь?
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[9]: Лямбда-выражения: зачем и почему
От: _FRED_ Черногория
Дата: 09.12.10 15:10
Оценка:
Здравствуйте, Tom, Вы писали:

_FR>>А зачем им тесты, если будут тесты на именованные методы?

Tom>Если у тебя в твоей именованной функции используется с 10-ок лямбд/анонимных методов итп то попробуй написать юнит тесты...

А какие будут сложности? Простой пример:

string M1(string left, string right) {
  return left + right;
}

void M2(string left, string right) {
  Func<string> r = () => left + right;
  return r();
}


Я просто не понимаю, в чём может быть разница в тестировании M1 и M2

Анонимные мотоды — это _локальные_ функции. Это означает, что извне их не видно и о них ничего не известно. Их не нужно тестировать. Тестировать в моём случае следует M1 и M2, а что уж там внутри — локальное дело метода.

Более того. в локальных функциях очень редко (если не сказать, что подчти никогда) не встречается не используемого кода, тогда как в обычных методах это не редкость, особенно в больших проектах. Поэтому "тестирабельны" (не специально, а опосредованно через внешний метод) анонимные методы "на ура".

Кстати, по поводу словосочетания "лямбд/анонимных методов": "лямбда" — это частный случай "анонимного метода", поэтому в таком виде фраза выглядит избыточной.
Help will always be given at Hogwarts to those who ask for it.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.