На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! В разделе обсуждаются следующие темы:
1) Процесс разработки программного обеспечения.
2) Определение требований к программному обеспечению.
3) Составные части и процесс проектирования (см. Шаблоны проектирования).
4) Документирование программного продукта(проекта).
5) Руководство разработкой программного обеспечения.
6) Проектирование пользовательского интерфейса.
7) Контроль версий проекта (см. Управление версиями в Subversion, Стратегии использования svn).
Модераторы: ElcnU
Страницы: (2) 1 [2]  все  ( Перейти к последнему сообщению )  
> Inversion of Control , не могу понять
    Цитата deil @
    ИМХО IoC определяется намного проще - это принцип, согласно которому мы пишем ПО так, что объекты "не падают с неба". Т.е. минимизируем кол-во new XXX().
    Ура, у нас есть новое слово - "компетенция". Твоё определение одновременно и уже, и размытее того, что я написал выше :-)
    Уже, потому что в моё определение включены любые решения, которые кто-то должен принять: буквально утверждается, что если ты пишешь какой-то класс и у него в каком-то методе принимается какое-то решение, принималку этого решения нужно вынести в виртуальный метод или вообще сделать абстрактный метод, чтоб юзер потом сам думал что ему нужно. Ну вот буквально паттерн template method. По состоянию на сегодня я такое проектирование отношу к соблюдению IoC. Ошибаюсь?
    Теперь про размытость - для конструирования N объектов, new, в любом случае, будет использован N раз. А вот интересные вопросы - где, когда, кем?
    Цитата deil @
    Если объекту А нужна ссылка на объект Б, это не в его компетенции знать, где взять нужную ему ссылку и уж тем более создать объект Б руками (в реальной жизни же ничего само по себе не материализуется). Всё остальное уже логично вытекает из этого утверждения.
    Гхм.
    ExpandedWrap disabled
      class A
      {
        public B TheB { get; set; } // A нужна ссылка на B
      }
       
      class B {}
      ...
      A a = new A();
      a.TheB = new B(); // объект класса А не принимает решения по поводу "где взять B", при этом имеем ужасный процедурный говнокод. Здесь IoC выполняется по-твоему?


    Цитата
    А интерфейсы и сокрытие реализации методов, в которые вы ударились - это ребята ООП :) Инкапсуляция.. IoC и интерфейсы никак не взаимосвязаны, вообще никак.

    Ты меня извини, но по-моему весь смысл проектирования ПО только и крутится вокруг интерфейса и реализации. Все паттерны, все принципы - говорят только о том кто кого должен видеть и как это должно происходить, а больше ничего и нет :-)
      Цитата andyag @
      Ура, у нас есть новое слово - "компетенция". Твоё определение одновременно и уже, и размытее того, что я написал выше :-)
      Уже, потому что в моё определение включены любые решения, которые кто-то должен принять: буквально утверждается, что если ты пишешь какой-то класс и у него в каком-то методе принимается какое-то решение, принималку этого решения нужно вынести в виртуальный метод или вообще сделать абстрактный метод, чтоб юзер потом сам думал что ему нужно. Ну вот буквально паттерн template method. По состоянию на сегодня я такое проектирование отношу к соблюдению IoC. Ошибаюсь?

      Считаю, что да, ошибаешься.

      Вернемся к примеру с БД.
      Задача: подсчитать кол-во пользователей в системе, у которых возвраст = ХХХ (select count(*) from users where age = ?)
      Супер-подробный абстрактный алгоритм:
      1. взять конфиг подключения к БД
      2. подключиться к БД
      3. узнать, в какой табличке лежат юзеры
      4. узнать, в каком поле лежит возраст
      5. создать запрос select count(*) from [users_table] where [age_column] = ?
      6. передать ему параметр
      7. выполнить запрос, получить ответ
      8. распарсить полученный ответ

      Следуя твоей логике, нам необходим некто, кто выдаст строку подключения к БД, некто кто сможет к ней подключиться (причемпущай нам пофиг если это MySQL, PostgreSQL, SQLite), объект\метод, который хранит информацию о метаданных, абстрактный объект SqlCommand, абстрактный объект SqlParameter (у нас же хз какая БД может быть! тут либо MySQLCommand, SQLiteCommand, etc) и парсилка ответов (ну вдруг нам вернут число в виде строки :)). Получим метод с 666 параметрами-интерфейсами на вход. Это не IoC, это overengineer'инг :))) Точнее, это шаблон template method, query, builder, называй как хочешь, но не сабж.

      Если мой код отвечает за подключение к БД и поиск всех пользователей с возврастом = 42, он за это и должен отвечать. Но все, что ему необходимо для решения своей задачи (обычно ресурсы и знания) он не сам возьмёт (рассчитает, спросит у кого-то а-ля SqlConnection.CreateCommand() и т.д.), а ему дадут их на вход.

      Добавлено
      Цитата andyag @
      Теперь про размытость - для конструирования N объектов, new, в любом случае, будет использован N раз. А вот интересные вопросы - где, когда, кем?

      Тут ты и ответил на вопрос данного треда (SRP vs IoC): IoC отвечает на вопросы где, когда, кем так - вне моего кода, до вызова, не мной.
      Паттерн template method отвечает по-другому: прямо тут, по требованию, мной. Но просто реализация этих действий (в данном случае - new XXX()) сокрыта от моих глаз через абстракции и наследование. И хотя коннешн создается специально отведенным объектом, который принимает кучу сложных решений, но финальное решение "получить коннекшн" всё-равно лежит на твоём коде. IoC оперирует именно такими решениями, из области control flow, а не из области инкапсуляции\полиморфизма.

      Добавлено
      Соот-но в твоем примере кода говнокода в классе А больше нет, он есть в коде вызова А. IoC не нарушается, но код - говно. Поэтому создание А мы должны обернуть во что-хочешь-так-и-называй и превратить в конфетку :)
        Спасибо за развёрнутый ответ :-)

        Цитата deil @
        Считаю, что да, ошибаешься.

        Вернемся к примеру с БД.
        Задача: подсчитать кол-во пользователей в системе, у которых возвраст = ХХХ (select count(*) from users where age = ?)
        Супер-подробный абстрактный алгоритм:
        1. взять конфиг подключения к БД
        2. подключиться к БД
        3. узнать, в какой табличке лежат юзеры
        4. узнать, в каком поле лежит возраст
        5. создать запрос select count(*) from [users_table] where [age_column] = ?
        6. передать ему параметр
        7. выполнить запрос, получить ответ
        8. распарсить полученный ответ

        Следуя твоей логике, нам необходим некто, кто выдаст строку подключения к БД, некто кто сможет к ней подключиться (причемпущай нам пофиг если это MySQL, PostgreSQL, SQLite), объект\метод, который хранит информацию о метаданных, абстрактный объект SqlCommand, абстрактный объект SqlParameter (у нас же хз какая БД может быть! тут либо MySQLCommand, SQLiteCommand, etc) и парсилка ответов (ну вдруг нам вернут число в виде строки :)). Получим метод с 666 параметрами-интерфейсами на вход. Это не IoC, это overengineer'инг :))) Точнее, это шаблон template method, query, builder, называй как хочешь, но не сабж.

        Если мой код отвечает за подключение к БД и поиск всех пользователей с возврастом = 42, он за это и должен отвечать. Но все, что ему необходимо для решения своей задачи (обычно ресурсы и знания) он не сам возьмёт (рассчитает, спросит у кого-то а-ля SqlConnection.CreateCommand() и т.д.), а ему дадут их на вход.
        Давай для начала сформулируем задачу.
        Требуется написать программу, которая будет подключаться к одной из нескольких баз (базы с разными движками) брать оттуда некоторую статистику и выводить на консоль. Такая формулировка пойдёт?

        Построение дизайна определяется:
        1. Множеством тех баз (источников) с которыми нам нужно работать
        2. Ожидаемыми перспективами проекта

        По поводу первого пункта. Можем ли мы утверждать, что работать придётся только с SQL-базами? Можем ли мы утверждать, что вообще речь идёт только о базах? Может текстовые файлы какого-нибудь экзотического формата тоже нужны? Можем ли мы утверждать, что в каждой базе есть понятие таблицы или чего-то очень близкого по значению? Ответив на все эти вопросы (возможно, ещё несколько), мы можем определиться с абстракцией. Есть 2 предельных случая:
        а. У нас все базы (источники) совершенно одинаковые, разница у них только в брендинге :-)
        б. Базы абсолютно разные, есть и SQL, и текстовые файлы, и нечто наше проприетарное.
        В случае а. мы можем начать строить абстракцию для алгоритма где-то на уровне "выполнить запрос и получить результат". Алгоритм при этом будет "идеально абстрактен" :-)
        В случае б. мы вообще ничего не можем обобщить. Абсолютно. Мы можем написать по конкретной реализации алгоритма на каждый тип источника данных.

        Второй пункт. Нам это нужно сопровождать? Это вообще программа для одного запуска или ей будут пользоваться? Насколько сильно планируется расширять? Здесь опять 2 предельных случая:
        а. Нужно написать, 1 раз запустить и забыть.
        б. Планируется развивать до чёрт знает чего.
        В случае а. у нас есть требования и о будущем думать не нужно. Если какая-то абстракция идеально подходит прямо сейчас, мы так и сделаем.
        В случае б. сразу нужно думать о будущем. Нужно быть осторожнее с абстракциями, т.к. сегодня у нас может быть только sql, а завтра появится что-то ещё.

        К какому совокупному предельному случаю ближе наша задача?
        аа. Пишем "в лоб", пишем так, чтоб просто хорошо писалось. Чтоб избежать глюков сегодня и чтоб не запутаться в течение 1 дня.
        аб. Нужно думать о будущем. Сейчас у нас только SQL. Завтра добавится какая-нибудь SQL-база, или текстовый файл?
        ба. Пишем "в лоб" - 10 реализаций алгоритма для разных баз.
        бб. Нужно думать о будущем, причём очень много и очень осторожно.

        В нашей задаче какая ситуация? :-) Ты говоришь о 666 параметрах у метода, но умалчиваешь о самом "проекте".

        666 параметров у метода говорят о том, что в системе есть место, где взаимодействую 666 элементов, причём никакие 2(или больше) элемента(ов) из этих 666 не олицетворяют какую-то подсистему нашей системы.

        Что я сказать-то пытаюсь.

        Всегда можно добиться того, чтобы у нашего метода было всего 2 параметра:
        "штука, которая инкапсулирует работу с конкретной базой" и "возраст"

        Если у нас базы только SQL, и SQL у всех одинаковый, то для нас SQL запрос может быть вообще строкой текста. И это будет нормально. Как только появляется какая-то разница между SQL у MsSql и SQL у MySql, пора вводить абстракцию. И абстракция эта должна быть достаточной, для того, чтобы скрыть разницу между разными базами для нашего алгоритма. Поэтому в случае, если у нас все базы sql, и если мы точно получаем информацию только через запрос select, и точно знаем, что во всех базах есть таблица users, и точно знаем, что в ней есть поле age, и точно знаем, что блаблабла, у нас "штука, которая инкапсулирует работу с конкретной базой" будет называться SqlQueryExecutor, единственное что от неё требуется - выполнять sql запрос (sql - в виде строки) и возвращать результат в каком-то унифицированном виде (например, DataTable).

        А если у нас противоположная ситуация, и мы в общем случае ни в чём не уверены, "штука, которая инкапсулирует работу с конкретной базой" будет называться UserStatisticsAnalyzer, которая будет давать нам метод Count(Condition where), который мы благополучно будем дёргать, передавая ему объект нашей реализации Condition. Т.е. буквально:
        ExpandedWrap disabled
          class WhereAgeIsXCondition : Condition
          {
            // тут реализация условия "возраст равен"
          };
           
          // а вот наш метод:
          int countUsersWithAge(UserStatisticsAnalyzer analyzer, int age) // мне пофигу, база там или не база, пофигу кеширует кто-то что-то или нет, вообще всё пофигу :-)
          {
            return analyzer.Count(new WhereAgeIsXCondition(age)); // а может тут вообще лямбдой можно обойтись
          }


        Как конструируется объект analyzer - не моё дело. В методе countUsersWithAge() проблем нет. Всё остальное тоже можно расписать таким образом. И НИГДЕ не будет 666 параметров. Я это гарантирую :-) :-) :-)

        SRP - выполняется, у countUsersWithAge() всего одна обязанность.
        IOC - выполняется, countUsersWithAge() принимает всего 1 решение - указывает методу Count какой предикат использовать.
          Сорри за мой ответ, но - и? :lol:

          Да, верю, SRP / IoC выполняются. Но код в 1 строчку не показателен всё же.
            Цитата deil @
            Сорри за мой ответ, но - и? :lol:

            Согласен, я не прорезюмировал :-)

            Речь идёт о твоих 666 аргументах. Если я правильно понял, ты имел в виду что-то вроде:
            DBConnectionConfig
            DBConnectionFactory
            DBTableResolver
            DBFieldResolver
            ...тут ещё 661...
            int age // ура :-)

            Я пытался сказать, что при вменяемом дизайне не может существовать такого куска кода (особенно в пределах одного метода), где друг с другом будут взаимодействовать 666 объектов :-) Если ты подумал, что я имел в виду такой подход - спешу тебя разуверить, я совсем не об этом говорил :-) Речь шла о том, чтобы всю функциональность разбросать по классам таким образом, чтобы каждый класс (да и метод тоже) знал только то, что ему нужно знать, а предоставлял только ту информацию, ради которой он изначально задумывался.

            Возьмём не 666 аргументов, а 4:
            ExpandedWrap disabled
              a b c d   // вот эти 4 аргумента
              \ / \ /
               e   f    // задумываемся о том, можно ли из групп элементов построить какие-то сущности, строим (повторяем, пока сущности выходят вменяемыми
                \ /     // как только доходим до первого ЖопоЗефирногоСокета, останавливаемся).
                 g      // вот результат (если повезло и голова работает правильно)


            Простой пример, если тебе нужно сделать запрос к базе и получить результат, тебе вообще говоря не нужно соединение с базой (DBConnection). Тебе нужна штука, которая сможет выполнять твой запрос и вернуть результат (QueryExecutor - а вот у неё внутри уже может быть DBConnection). Теперь QueryExecutor. Ему нужна штука, которая может дать ему подключение к базе (DBConnectionResolver). Отлично. DBConnectionResolver'у нужна какая-то штука, которая знает настройки подключения к базе (DBConnectionConfigurationResolver). Тут остановимся (банально надоело :-) ). В результате имеем:

            ExpandedWrap disabled
              // ...где-то, не факт, что исключительно ради вызова нашего метода...
              DBConnectionConfigurationResolver dbConnectionConfigResolver = DBConnectionConfigurationResolver.Instance; // ...
              DBConnectionResolver dbConnectionResolver = new DBConnectionResolver(dbConnectionConfigResolver); // ему нужен только конфиг, умеет подключаться куда надо
              QueryExecutor queryExecutor = new QueryExecutor(dbConnectionResolver); // ему нужно только подключение, умеет выполнять запросы
              // (ты веришь, что я смогу в итоге получить объект со сколь угодно кастомным поведением, при это сам объект об этом никогда не догадается?)
              //
               
              // вызов нашего волшебного метода
              // ему нужны только "исполнитель запроса" и "возраст"
              int personCount = countPersonsWithAge(queryExecutor, 42); // только их и даём
              // никакой магии - годы тренировок и ловкость рук :-)
              // queryExecutor - это объект с ОЧЕНЬ сложным поведением, при этом класс QueryExecutor (да и все остальные)
              // просты, как семейные трусы.

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

            Сопоставляем эту формулировку и пример выше. IoC выполняется, т.к. отдельные классы отвечают только за себя, никакими 666 аргументами тут и не пахнет, более того, явно видно, что при сопровождении кода с таким же подходом код будет усложняться только количественно (больше сущностей, больше строчек код), а качественная сложность будет оставаться постоянной (любой класс зависит от пары интерфейсов и не более того).

            Тут резюме заканчивается :-)

            Цитата deil @
            код в 1 строчку не показателен всё же.

            Сейчас вспомнил свои сложности с "рисованием треугольника общего вида" (всё время то равнобедренный, то прямоугольный получаются), задумался на тему того, как должен выглядеть показательный код (код общего вида :D ).
              Начнем с того, что ты 666 параметров заменил 666-ю абстрактными классами\интерфейсами :yes:
              И все-равно написал вот этот код:
              ExpandedWrap disabled
                DBConnectionConfigurationResolver dbConnectionConfigResolver = DBConnectionConfigurationResolver.Instance; // ...
                DBConnectionResolver dbConnectionResolver = new DBConnectionResolver(dbConnectionConfigResolver); // ему нужен только конфиг, умеет подключаться куда надо
                QueryExecutor queryExecutor = new QueryExecutor(dbConnectionResolver);


              Шило на мыло, в данном конкретном примере :)

              Но неважно.

              ExpandedWrap disabled
                def factorial(n)
                  return 1 if n == 1
                 
                  if n > 666
                    puts "warning: calculation will be slow"
                  end
                 
                  return n * factorial(n-1)
                end


              Считаем факториал. Решение "вывести на экран предупреждение, если входное число больше 666" - мы "не можем его не принимать"? Для рассчета факториала данный if - явно лишний и не жизненно необходимый. Но он есть, потому что я так захотел.
              Всё, мы нарушили IoC?

              Добавлено
              А теперь?
              ExpandedWrap disabled
                class SixSixSixWarningCondition
                  def show_warning?(n)
                    return (n > 666)
                  end
                end
                 
                def factorial(n)
                  return 1 if n == 1
                  return n * factorial(n-1)
                end
                 
                def factorial_with_warning(n)
                  cond = SixSixSixWarningCondition.new()
                  
                  if cond.show_warning?(n)
                    puts "warning: calculation will be slow"
                  end
                 
                  return factorial(n)
                end


              Добавлено
              Во :yes:
              Сообщение отредактировано: deil -
                deil, первый код от второго принципиально не отличается, ибо оба написаны "в процедурном стиле"
                  Ну давайте ещё "процедурный стиль" обмусолим :wub:
                  Итак, что не так с кодом? Почему к нему нельзя применять определение "IoC"?
                  Сообщение отредактировано: deil -
                    Цитата deil @
                    Шило на мыло, в данном конкретном примере :)

                    Да тут же принципиальная разница:
                    В твоём случае речь идёт о взаимодействии 666 объектов в одной точке, в моём случае - во всём проекте.
                    В твоём случае речь о том, что на поведение единицы влияет 666 факторов, в моём случае на поведение единицы влияют 1-2-3 фактора.

                    Цитата deil @
                    Считаем факториал. Решение "вывести на экран предупреждение, если входное число больше 666" - мы "не можем его не принимать"? Для рассчета факториала данный if - явно лишний и не жизненно необходимый. Но он есть, потому что я так захотел.
                    Всё, мы нарушили IoC?
                    Твой пример показателен. Это реально хреновый код :-) Сколько ворнингов выведет factorial(1000)? А всё потому что "ты так захотел" :-)
                    Правильное решение - написать отдельно вменяемый факториал без принта и отдельно враппер, который выводит ворнинг в нужном случае и в зависимости от контекста использовать то или другое. Врапперов с принтом при этом можно сделать несколько штук - для вывода на консоль, для пиканья спикером и т.д. Твоя реализация - идеально нерасширяемая.

                    И да, IoC тут нарушается из-за того, что алгоритм вычисления факториала знает что такое "консоль" (если я правильно трактую Руби, который я не знаю).

                    Добавлено
                    ExpandedWrap disabled
                      using System;
                      using System.Collections.Generic;
                      using System.Text;
                       
                      namespace cs_ioc2
                      {
                          class Math
                          {
                              public static int Factorial(int i)
                              {
                                  if (i == 0)
                                  {
                                      return 1;
                                  }
                       
                                  if (i == 1)
                                  {
                                      return 1;
                                  }
                       
                                  return i * Factorial(i - 1);
                              }
                          }
                       
                          interface FactorialCalculator
                          {
                              int calculateFactorial(int i);
                          }
                       
                          class SimpleFactorialCalculator : FactorialCalculator
                          {
                              public int calculateFactorial(int i)
                              {
                                  return Math.Factorial(i);
                              }
                          }
                       
                          class ConsoleWarningFactorialCalculator : FactorialCalculator
                          {
                              public int calculateFactorial(int i)
                              {
                                  if (i > 10)
                                  {
                                      Console.WriteLine("may be slow");
                                  }
                       
                                  return Math.Factorial(i);
                              }
                          }
                       
                          class Program
                          {
                              // мне пофигу, мне нужно вычислить факториал
                              // кто, как и куда - мне ПОФИГУ
                              static int myAlgorithm(FactorialCalculator calc)
                              {
                                  return 123 + calc.calculateFactorial(11);
                              }
                       
                              static void Main(string[] args)
                              {
                                  // вычислим с ворнингом
                                  FactorialCalculator calc = new ConsoleWarningFactorialCalculator();
                                  Console.WriteLine("{0}", myAlgorithm(calc));
                       
                                  // вычислим без ворнинга
                                  FactorialCalculator calc2 = new SimpleFactorialCalculator();
                                  Console.WriteLine("{0}", myAlgorithm(calc2));
                              }
                          }
                      }
                      И с этой стороны код говно, и с другой =) Ужас, стыдно :rolleyes:

                      Мы ушли от темы и в этом болоте стало сложнее выражать точку зрения.

                      Итак, чуть назад:
                      Цитата
                      Уже, потому что в моё определение включены любые решения, которые кто-то должен принять: буквально утверждается, что если ты пишешь какой-то класс и у него в каком-то методе принимается какое-то решение, принималку этого решения нужно вынести в виртуальный метод или вообще сделать абстрактный метод, чтоб юзер потом сам думал что ему нужно. Ну вот буквально паттерн template method. По состоянию на сегодня я такое проектирование отношу к соблюдению IoC. Ошибаюсь?


                      Давай про факториал, какие решения могут приниматься?
                      1. куда выводить ворнинг - консоль, файл, етц; Реализация принятия данного решения - в интерфейсе с методом Write и выборе правильной реализации.
                      2. в каких случаях выводить ворнинг. Реализация принятия данного решения - тупой if (ну, в коде а-ля-говнокод-от-deil'а)

                      По твоей трактовке, метод рассчета факториала должен выглядеть так:
                      ExpandedWrap disabled
                        int Calculate(int n, IOutputStream output, Condition condition) {}

                      Я прав?

                      Именно здесь я и не соглашаюсь. Принимаемые решения могут быть двух типов: одни связаны с поиском и разрешением зависимостей (ресурсы, входные параметры, етц), другие - на уровне бизнес-логики.

                      Условие 2 (в каких случаях выводить ворнинг) - это бизнес-логика.
                      Я не хочу давать пользователю моего класса поле для маневров, мне не нужно вообще заморачиваться с его расширяемостью и прочим - моё ПО вообще для варки кофе! Поэтому я не делаю абстрактный класс Condition, не реализую специфичный My666Condition, который наследуется от и уточняет PropertyCondition. И не трачу время на враппер для своего метода, который подсовывает конкретный объект Condition. И параллельно не нарушаю IoC.
                        Цитата deil @
                        По твоей трактовке, метод рассчета факториала должен выглядеть так:

                        Посмотри, пожалуйста, в моё предыдущее сообщение, я там дописал код, который иллюстрирует мою точку зрения :-)

                        Цитата deil @
                        Я прав?

                        Ды нет же :-)


                        Цитата deil @
                        Именно здесь я и не соглашаюсь. Принимаемые решения могут быть двух типов: одни связаны с поиском и разрешением зависимостей (ресурсы, входные параметры, етц), другие - на уровне бизнес-логики.

                        Условие 2 (в каких случаях выводить ворнинг) - это бизнес-логика.
                        Да, это бизнес логика. И эта бизнес логика может быть реализована сотней способов в сотне разных мест. И что самое интересное, все 100 способов будут работать. Только вот в большинстве случаев сопровождать это будет тяжело.


                        Цитата deil @
                        Я не хочу давать пользователю моего класса поле для маневров, мне не нужно вообще заморачиваться с его расширяемостью и прочим - моё ПО вообще для варки кофе!
                        Судя по коду, ты и себе не хочешь оставлять поле для манёвров :-) Про кофе - нонсенс. Программы пишутся одинаково, независимо от кофе, чая или кефира. Есть domain-specific сущности, есть бизнес логика, которая эти сущности связывает - ЭТО решение задачи. А всё что ниже НУ НИКАК не может зависеть от кофе :-)


                        Цитата deil @
                        Поэтому я не делаю абстрактный класс Condition, не реализую специфичный My666Condition, который наследуется от и уточняет PropertyCondition. И не трачу время на враппер для своего метода, который подсовывает конкретный объект Condition. И параллельно не нарушаю IoC.
                        Твой код не показателен, не хватает клиента, которому нужно вычислить факториал (например, алгоритма, которому этот факториал нужен + вызова самого алгоритма).
                          Цитата
                          Судя по коду, ты и себе не хочешь оставлять поле для манёвров :-) Про кофе - нонсенс. Программы пишутся одинаково, независимо от кофе, чая или кефира. Есть domain-specific сущности, есть бизнес логика, которая эти сущности связывает - ЭТО решение задачи. А всё что ниже НУ НИКАК не может зависеть от кофе :-)

                          Мы ведь о другом сейчас беседуем, разве нет? Не вижу смысла задр-ивать на код и пытаться сделать его похожим на умные книжки.

                          Принцип IoC отличается от твоего определения - ты слишком усложнил и сузил. ПО по твоим критериям несомненно будет отвечать принципу IoC, но окажется слишком замороченным и утонет в количестве никому не нужных сущностей (см. первый абзац). На самом деле критерии более слабые (на самом деле критериев нет, это твоя терминология. Принципу IoC нельзя удовлетворять, потому что это просто подход к проектированию для уменьшения зависимостей между компонентами, а не какое-то свойство системы да\нет). И упомянаются в них (критериях) не все принимаемые объектом решения, а лишь решения по разрешению зависимостей. Логика - сбоку. IoC не заставляет тебя выносить всё подряд в интерфейсы и предусматривать кучу частных случаев, лепить миллионы точек расширения. А ты давишь именно на это зачем-то.
                            Цитата deil @
                            Мы ведь о другом сейчас беседуем, разве нет? Не вижу смысла задр-ивать на код и пытаться сделать его похожим на умные книжки.

                            Не знаю. К чему ты тогда привёл аргумент:
                            Цитата deil @
                            мне не нужно вообще заморачиваться с его расширяемостью и прочим - моё ПО вообще для варки кофе!

                            ?

                            Цитата deil @
                            На самом деле критерии более слабые (на самом деле критериев нет, это твоя терминология. Принципу IoC нельзя удовлетворять, потому что это просто

                            Т.е. как "нет критериев"? А что тогда вообще есть? И что значит "нельзя удовлетворять"? Что в твоём понимании вообще описывает IoC?

                            Цитата deil @
                            И упомянаются в них (критериях) не все принимаемые объектом решения, а лишь решения по разрешению зависимостей. Логика - сбоку.

                            Вот, пошла конкретика. Начнём с того, что ты только что обвинил меня в зауживании, но при этом сам дал ввёл на порядок более узкий критерий. Покажи где ты читаешь этот список критериев? Вот, читаем Фаулера:
                            Цитата
                            Inversion of Control is a key part of what makes a framework different to a library. A library is essentially a set of functions that you can call, these days usually organized into classes. Each call does some work and returns control to the client.

                            A framework embodies some abstract design, with more behavior built in. In order to use it you need to insert your behavior into various places in the framework either by subclassing or by plugging in your own classes. The framework's code then calls your code at these points
                            Ты обвиняешь меня в "зауживании", при этом Фаулер, расмматривая чисто "библиотеку" и "фреймворк" умудряется утверждать, что речь, вообще говоря, идёт о любых аспектах поведения - я не вижу здесь той конкретики, которую приводишь ты. Откуда она?

                            Вот Мартина читаем:
                            Цитата
                            A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW
                            LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
                            B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS
                            SHOULD DEPEND UPON ABSTRACTIONS.

                            Опять не вижу того, что говоришь ты.

                            Цитата deil @
                            IoC не заставляет тебя выносить всё подряд в интерфейсы и предусматривать кучу частных случаев, лепить миллионы точек расширения. А ты давишь именно на это зачем-то.
                            Я ж и не спорю про миллион точек расширения. И тот код, который я тебе показал - это явный оверинжиниринг. Смысл того кода, просто показать суть сказанного. В реальной жизни, естественно, так только педанты пишут.
                              Более узкий (строгий) критерий - критерий, которому удовлетворяет меньшее число объектов. Перечитай ещё раз свое определение :) Оно более строгое (чуть лишний if и всё).

                              Про Фаулера: зри в корень! Ты мне цитируешь framework vs library. Да, здесь ни слова о том, о чем я говорю :)

                              И, соответственно, парой абзацев выше там есть понятное определение IoC (и даже пример кода, который написано в процедурном стиле, нерасширяем никак и вообще не использует ни одной абстракции, а завязан на конкретную реализацию). Я с ним (определением) на 100% согласен.
                              Сообщение отредактировано: deil -
                                Цитата deil @
                                Более узкий (строгий) критерий - критерий, которому удовлетворяет меньшее число объектов. Перечитай ещё раз свое определение :) Оно более строгое (чуть лишний if и всё).
                                Теория всегда более строга, чем практика. Если учесть, что любую систему можно поделить на подсистемы огромным числом способов, а при этом каждая подсистема является системой сама по себе, никто не запрещает (и не требует!) применять "принципы" ко всей системе сразу. У меня может быть чудовищная программа, ужасный говнокод, но если её GUI написан на .NET, один из аспектов моей программы будет удовлетворять IoC на все 100%!

                                Цитата deil @
                                Про Фаулера: зри в корень! Ты мне цитируешь framework vs library. Да, здесь ни слова о том, о чем я говорю :)
                                И, соответственно, парой абзацев выше там есть понятное определение IoC
                                http://martinfowler.com/bliki/InversionOfControl.html
                                Не увидел определения :-)

                                Цитата deil @
                                (и даже пример кода, который написано в процедурном стиле, нерасширяем никак и вообще не использует ни одной абстракции, а завязан на конкретную реализацию).

                                Хорошо, давай рассмотрим его иллюстрацию:

                                Типа тут нет IoC:
                                ExpandedWrap disabled
                                  puts 'What is your name?'
                                  name = gets
                                  process_name(name)
                                  puts 'What is your quest?'
                                  quest = gets
                                  process_quest(quest)

                                1. Принимается решение о тексте вопросов
                                2. Принимается решение об использовании консоли для ввода и вывода
                                3. Принимается решение о том, кто должен обработать ввод пользователя
                                4. Принимается решение о последовательности вопросов

                                А тут типа есть:
                                ExpandedWrap disabled
                                  require 'tk'
                                  root = TkRoot.new()
                                  name_label = TkLabel.new() {text "What is Your Name?"}
                                  name_label.pack
                                  name = TkEntry.new(root).pack
                                  name.bind("FocusOut") {process_name(name)}
                                  quest_label = TkLabel.new() {text "What is Your Quest?"}
                                  quest_label.pack
                                  quest = TkEntry.new(root).pack
                                  quest.bind("FocusOut") {process_quest(quest)}
                                  Tk.mainloop()

                                1. Принимается решение о тексте вопросов
                                2. Принимается решение об использовании Tk для ввода и вывода
                                3. Принимается решение о том, кто должен обработать ввод пользователя
                                4. Не принимается решение о последовательности вопросов

                                Пункт 1 и в первом, и во втором случае вычёркиваем - одинаково.
                                Пункт 2 тоже одинаковый, т.к. в обоих случаях речь идёт о специфике UI.
                                Пункт 3 вычёркиваем - одинаково.
                                Пункт 4 остаётся: в первом случае у нас вопросы задаются последовательно (есть последовательность, выполнение этого куска кода завершится или если есть 2 ответа, или аварийно), во втором случае всего этого нет - юзер "может" сделать то, о чём его просят, но он не обязан это делать (нет последовательности, кусок кода можно завершить аварийно и неаварийно, гарантии результата нет).

                                Вот именно 4ый пункт он позиционирует как IoC. Что произошло? В процедурном коде было 4 решения, здесь - 3. Избавились от одного решения, получили IoC (его позиция). Думаю пример не очень хороший, т.к. программы ведут себя абсолютно по-разному - предоставляют пользователю разные возможности (провокация, короче). Но даже в этом случае:
                                Цитата andyag @
                                IoC - это принцип построения дизайна таким образом, что любая функциональная единица (модуль, класс, метод) принимает только те решения по поводу своего поведения, которые она не может не принимать.

                                Если мы сейчас вспомним, что живём в реальном мире, то на 3 одинаковых пункта можно смело закрыть глаза (ну говнокод, хрен с ним). Рассматриваем только 4ый пункт, и (о чудо!) осознаём, что во втором случае код не принимает то решение, которое он не обязан был принимать (решение о последовательности задавания вопросов).

                                Если рассматривать аспект "порядок задавания вопросов" второго куска кода, IoC в нём выполняется. Если рассмотреть другие аспекты - не выполняется. Он пофиксил всего 1 аспект, а остальные как были говнокодом, так им и остались :-) Если для задачи какой-то аспект - это 90% всей задачи, на остальные аспекты можно закрыть глаза (привязка к конкретному движку UI, захардкоженый текст, и т.д.) - тупо правило 20-80.

                                Тут просто фишка в том, что изначально он не сказал "вопросы могуть быть заданы/отвечены в любом порядке", а потом вывернул так, будто это подразумевалось. Т.е. речь идёт о том, что первая реализация брала на себя не только принятие решений за какой-то там другой код, но и за самого ПОЛЬЗОВАТЕЛЯ - она "выбрала" за него порядок вопросов.

                                Цитата deil @
                                Я с ним (определением) на 100% согласен.

                                Ну покажи определение-то :-) Ну не вижу я его в этой статье.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0640 ]   [ 15 queries used ]   [ Generated: 2.05.24, 07:20 GMT ]