Львиная доля программистов с чистой совестью заявит, что предпочитает решать задачи просто, руководствуясь прежде всего здравым смыслом. Вот только это "просто" у каждого свое и как правило отличное от других. После одного долгого и неконструтивного спора с коллегой я решил изложить, что именно считаю простым сам и почему. Это не привело к немедленному согласию, но позволило понять логику друг друга и свести к минимуму лишние дискуссии.


Первый критерий


Особенности мозга человека таковы, что он плохо хранит и отличает более 7-9 элементов в одном списке при оптимальном их количестве 1-3.


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


Второй критерий


Самое важное — в первых двух строках любого класса.


  1. Имя, параметры типа, реализованные интерфейсы.
  2. Параметры конструктора.

В результате мы знаем о классе все, что необходимо для его использования (имя, параметры, входы, выходы), даже не заглядывая в оставшийся код.
Разумеется, это работает при условии ограничений:


  1. Избегайте наследования реализаций
  2. Избегайте внедрения зависимостей иначе, чем через параметры конструктора.

И этот критерий может быть реализован средствами статического анализа.


Третий критерий


Один тип — одна задача. Контракты — интерфейсам, реализации — классам, алгоритмы — методам!
Мультитулы хороши только если ничего другого под рукой вообще нет. Благодаря первым двум критериям писать такие типы несложно. Да, это принцип единственной ответственностиразделения интерфейсов до кучи)
А вот здесь статический анализ придется доверить человеку.


Четвертый критерий


Ограничения — такая же легальная часть контракта, как и сигнатуры методов.
И следовательно...


  1. Комментарии в контракте — почти всегда полезны, комментарии в коде реализации почти всегда вредны.
  2. DTO — полноценные объекты, чья примитивность поведения вознаграждается автоматической сериализацией.
  3. Неизменяемые объекты — вознаграждают удобством валидации, отсутствием гонок и лишнего копирования.
  4. Статический метод — полноценный класс без состояния, все плюшки от неизменяемых объектов плюс меньший трафик памяти.
  5. Анонимные делегаты и лямбды — заменяют связку интерфейс-класс с одним методом, позволяя выкинуть два лишних типа и продемонстрировать сигнатуру вызова прямо в коде.
  6. Добавьте остальные "ненастоящие объекты" по вашему вкусу.

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


Пятый критерий


Максимальное удобство для повторного использования.
Применяйте наследование интерфейсов, избегайте наследования реализаций и получите сразу три источника повторного использования кода


  1. Повторное использование контрактов — очень полезно, даже если ни одна готовая реализация не подошла.
  2. Повторное использование реализаций — везде где принимается контракт можно задействовать уже написанную реализацию.
  3. Повторное использование клиентского кода — весь код, работающий с интерфейсом (включая тесты), можно переиспользовать с каждой новой реализацией.

Да, это принцип подстановки Лисков в максимально простой и практичной форме.
Статический анализатор может сообщать об использовании нежелательного наследования реализации.
Также не стоит прятать типы или методы "на всякий случай". Публичным должно быть все, кроме того, что необходимо спрятать как детали реализации.


Шестой критерий


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


Седьмой критерий


Типы параметров конструктора должны как можно больше говорить о том, как надо использовать их значение.
Например:


  1. IService — служба, необходимая для реализации
  2. Lazy<IService> — служба, которой может и не быть при старте, для начала использования надо прочитать свойство Value, при первом обращении возможна пауза.
  3. Task<IResource> — ресурс, который получается асинхронно
  4. Func<IArgument, IResource> — параметризованная фабрика ресурсов
  5. Usable<IResource> — ресурс, который нужен до определенного момента, об окончании использования можно сообщить, вызвав метод Dispose.

Увы, статический анализ мало чем тут поможет.


Восьмой критерий


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


Девятый критерий


Зависимости типов между собой необходимо ограничивать


  1. Циклов в графе типов надо избегать
  2. Также надо избегать прямых ссылок реализаций друг на друга.
  3. Крайне желательно использовать паттерны разбиения типов на слои, добавляя ограничение на зависимости между типами разных слоев друг на друга.

Целью является максимальное упрощение графа зависимостей типов. Это помогает как при навигации по коду, так и при определении возможного аффекта от тех или иных изменений.
Да, в этот критерий входит принцип инверсии зависимостей.
Да, тут может помочь статический анализ.


Десятый критерий


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


Одиннадцатый критерий


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


Итоги


Письменное изложение собственных представлений многое прояснило для меня самого.
Хотелось бы увидеть ваши критерии простоты в комментариях.
Упоминания статического анализа означают возможность реализации, а не ее наличие на текущий момент.
Дополнения и критика традиционно приветствуются.

Поделиться с друзьями
-->

Комментарии (86)


  1. asdf87
    18.10.2016 04:29
    +4

    Про 1-3 метода, свойства и т. п., мне кажется, это перебор. Минимальная сложность решения задачи эквивалентна сложности самой задачи и не всегда можно 1 цельный логический объект разбить на кучу маленьких идеальных объектов по 1-3 метода. Если у тебя 5 связанных свойств в объекте, то методов для работы с ними скорее всего будет еще больше, а при попытке разбить этот объект на более мелкие прийдется как минимум это состояние в 5 полей пробрасывать между мелкими объектами, что возможно еще и к нарушению инкапсуляции приведет.


    1. Bonart
      18.10.2016 08:32
      +1

      Идеал не всегда достижим, но совершенно нормально знать о нем и стремиться к нему.


      1. Oxoron
        18.10.2016 10:04
        +1

        Лучшее — враг хорошего.

        Вы написали советы, описали плюсы, указали что детектируется до\во время компиляции. В итоге в статье не хватает двух важных моментов: недостатков от ваших советов, а также границ применимости (или хотя бы контрпримеров). На мой взгляд, статья «Когда не нужно следовать этим советам» будет не менее полезна, чем ваша. Хотя и ваша содержит немало здравых мыслей.


        1. Bonart
          18.10.2016 11:12

          недостатков от ваших советов

          Главная особенность указана — будет много маленьких типов.


          а также границ применимости

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


          1. Oxoron
            18.10.2016 13:14

            Главная особенность указана — будет много маленьких типов.

            Почему вы не указали лишь главного преимущества, а указали плюсы от применения каждого из критериев?

            Теперь собственно по недостаткам и областям применения.
            1. Всякого рода конвертеры часто содержат в себе десятки методов. Есть еще перегрузки типа AddParameter(long parameter), AddParameter(int parameter),… Есть DTO и прочие классы для обмена данными, регулярно (де)сериализуемые и содержащие десятки полей.

            2.
            Избегайте наследования реализаций

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

            И наслаждайтесь инициализацией тестовых классов. В каждом тесте.

            4.
            Статический метод — полноценный класс без состояния, все плюшки от неизменяемых объектов плюс меньший трафик памяти.

            Статический метод частенько зависит от состояния своего класса.

            5.
            комментарии в коде реализации почти всегда вредны.

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

            6. А как же YAGNI? Развернутый ответ — тут.

            И это еще lair не высказался.

            Еще раз: ваши критерии хороши, добавления инфы про статический анализ в такого рода статьях я ранее вообще не встречал, но нет простой мысли: ничто не дается бесплатно. И часто хочется четко понимать, чем же я заплачу за выполнения каждого критерия.


            1. Bonart
              18.10.2016 14:19
              +1

              Всякого рода конвертеры часто содержат в себе десятки методов.

              Не могу сказать что это хорошо.


              Надеюсь, вы не предлагаете избегать использования абстрактных классов?

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


              И наслаждайтесь инициализацией тестовых классов. В каждом тесте.

              Как будто что-то плохое.


              Есть еще перегрузки типа AddParameter(long parameter), AddParameter(int parameter)

              Для таких вещей предпочитаю обобщения и методы расширения.


              Есть DTO и прочие классы для обмена данными, регулярно (де)сериализуемые и содержащие десятки полей.

              Такое предпочитаю разбивать.


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

              Все это входит в "почти".


              А как же YAGNI?

              Сам по себе не нужен. Он защищает от перепроектирования, но SRP с этой задачей справляется лучше.


              И часто хочется четко понимать, чем же я заплачу за выполнения каждого критерия.

              Большим количеством маленьких типов и, возможно, недостатком производительности там, где это критично. Специально отбирал средства без серьезных побочных эффектов.


              И это еще lair не высказался

              С ним традиционно заруба, иногда на трехзначное число комментариев.


              1. Oxoron
                18.10.2016 15:27

                -Есть еще перегрузки типа AddParameter(long parameter), AddParameter(int parameter)
                -Для таких вещей предпочитаю обобщения и методы расширения.


                Ok, есть класс для формирования запросов к AzureStorage, в нем
                12 публичных методов
                public AzureStorageFilter In(string column, IEnumerable<string> values)
                public AzureStorageFilter In(string column, IEnumerable<int> values)
                public AzureStorageFilter In(string column, IEnumerable<long> values)
                public AzureStorageFilter In(string column, IEnumerable<DateTime> values)
                public AzureStorageFilter In(string column, IEnumerable<Guid> values)
                public AzureStorageFilter In(string column, IEnumerable<double> values)
                
                public AzureStorageFilter In(string column, params string[] values)
                public AzureStorageFilter In(string column, params int[] values)
                public AzureStorageFilter In(string column, params long[] values)
                public AzureStorageFilter In(string column, params DateTime[] values)
                public AzureStorageFilter In(string column, params Guid[] values)
                public AzureStorageFilter In(string column, params double[] values)
                


                1. Bonart
                  18.10.2016 15:40

                  Переделаю на что-то вроде


                  public AzureStorageFilter In(this Column column, IEnumerable<string> values)


                  1. Oxoron
                    18.10.2016 15:48

                    Это один метод. А у нас 10 групп по 6 методов. Что делать с оставшимися 59 методами?


                    1. Bonart
                      18.10.2016 16:56

                      То же самое. На методы расширения правило о числе методов в классе не действует.
                      Плюс, судя по сигнатурам, и там поможет кодогенерация.


                      1. Oxoron
                        18.10.2016 17:22

                        Отлично, мы выяснили, что в методах расширения может быть сколько угодно методов. Точнее, сколько нужно.
                        Вопрос: чем методы расширения лучше обычных методов? У них есть один недостаток: они не имеют доступа к приватным полям обрабатываемых объектов (если они не nested, что в нашем случае бессмысленно).


                        1. Cryvage
                          19.10.2016 12:33

                          Если задача — сократить количество методов, то можно сделать так:

                          public AzureStorageFilter In<T>(string column, IEnumerable<T> values) { }
                          public AzureStorageFilter In<T>(string column, params T[] values) { }
                          

                          Впрочем, применимость такого подхода зависит от того, насколько разнится реализация всех этих перегрузок.


                          1. Oxoron
                            19.10.2016 12:59
                            +1

                            У нас T ограничено (как правило) int, double, long, Guid, DateTime. Generic не сработает.


                        1. Bonart
                          19.10.2016 13:05

                          Вопрос: чем методы расширения лучше обычных методов? У них есть один недостаток: они не имеют доступа к приватным полям обрабатываемых объектов (если они не nested, что в нашем случае бессмысленно).

                          Методы расширения против методов контракта, плюсы:


                          1. Методы расширения не зависят от реализации
                          2. Публичные контракты методов расширения не зависят друг от друга (можно рассматривать каждый метод расширения как отдельный класс без состояния).
                          3. Добавление методов расширения не меняет контракт.
                          4. Метод расширения свободно переиспользуется всеми реализациями и любым клиентским кодом.

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


                          1. Oxoron
                            19.10.2016 13:49

                            1. При переносе метода из класса в «расширение» просто меняется зависимость: была зависимость от this, стала от (this ArgumentType ArgumentValue).
                            2. Контракты обычных классов зависят друг от друга?
                            3. Вместо изменения контракта основного класса меняется контракт класса с расширениями.
                            4. Метод основного класса не может быть переиспользован?

                            Методы расширения позволяют расширять функциональность класса без изменения самого класса, об этом даже название говорит. Иногда они позволяют писать более выразительный код, иногда более короткий.

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

                            И да, изначальный вопрос был «Почему мы можем иметь много методов расширения в классе, но ограничены 9 обычными методами на класс».


                            1. Bonart
                              19.10.2016 14:28

                              При переносе метода из класса в «расширение» просто меняется зависимость

                              Меняется две зависимости — контракт исходного типа перестает зависеть от метода, метод перестает зависеть от реализации контракта.
                              Двойной профит.


                              Контракты обычных классов зависят друг от друга?

                              Методы одного контракта друг от друга в общем случае зависят. И точно не зависят от методов расширения.


                              Вместо изменения контракта основного класса меняется контракт класса с расширениями.

                              Нет никакого "общего контракта класса с расширениями"!
                              Есть отдельные контракты — контракт класса и по одному контракту на метод расширения.


                              Метод основного класса не может быть переиспользован?

                              Добавление метода класса — изменение контракта класса. С аффектом в виде возможной регрессии, нарушениями принципов единственной ответственности и разделения интерфейсов. И это ДО любого переиспользования. Добавление метода интерфейса — все то же самое, еще и помноженное на число реализаций.


                              Платить за них приходится размазыванием логики по нескольким классам

                              Как будто что-то плохое.


                              и некоторыми проблемами с доступом

                              Это не проблема, а ограничение: если методу нужны закрытые данные — он не может быть методом расширения.


                              И да, изначальный вопрос был «Почему мы можем иметь много методов расширения в классе, но ограничены 9 обычными методами на класс».

                              Ответ все тот же — обычные методы меняют контракт, методы расширения добавляют каждый (!) по новому контракту, которые можно использовать независимо друг от друга.


                              1. Oxoron
                                19.10.2016 15:25

                                Меняется две зависимости — контракт исходного типа перестает зависеть от метода, метод перестает зависеть от реализации контракта.

                                Контракт библиотеки остался прежним, при переносе один контракт разбился на два. Далеко не факт, что это к лучшему. Если метод использовал какую-то реализацию до переноса — он её использует и после переноса, и значит зависит от нее. Вот только теперь разработчик основного класса должен поддерживать не только основной класс, но и расширения. Сомнительный профит.

                                Методы одного контракта друг от друга в общем случае зависят. И точно не зависят от методов расширения.

                                Если вы перенесли метод из основного класса в расширение — все зависящие от него методы все равно от него зависят.

                                Есть отдельные контракты — контракт класса и по одному контракту на метод расширения.

                                В итоге у нас вместо одного большого контракта куча атомарных. И я бы не сказал, что это хорошо. Малина в банке практичнее малины на полу.

                                Добавление метода класса — изменение контракта класса.

                                Добавление метода расширения — создание нового контракта (и реализации). Вы еще не забыли, что мы не можем зависеть больше чем от 3 контрактов\реализаций? И да, добавление методов к контракту совершенно необязательно влияет на переиспользование уже существующих.

                                — Платить за них приходится размазыванием логики по нескольким классам
                                — Как будто что-то плохое.

                                Вполне возможно, что плохое. Не очень удобно метаться по десятку классов при анализе кода. Человек, знаете ли, не может держать в голове больше 7-9 уровней вложения кода.

                                Ответ все тот же — обычные методы меняют контракт, методы расширения добавляют каждый (!) по новому контракту, которые можно использовать независимо друг от друга.

                                То есть, раньше я зависел от одного большого контракта на 60 методов, теперь завишу от 20 малых (по методу в каждом). А больше 9 зависимостей для класса — зло. Finita.


                                1. Bonart
                                  20.10.2016 01:10
                                  +1

                                  Контракт библиотеки остался прежним, при переносе один контракт разбился на два. Далеко не факт, что это к лучшему.

                                  Да неужели? Основной контракт (требующий реализации в виде объекта) стал меньше и проще.


                                  Если метод использовал какую-то реализацию до переноса — он её использует и после переноса, и значит зависит от нее. Вот только теперь разработчик основного класса должен поддерживать не только основной класс, но и расширения. Сомнительный профит.

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


                                  Вполне возможно, что плохое. Не очень удобно метаться по десятку классов при анализе кода. Человек, знаете ли, не может держать в голове больше 7-9 уровней вложения кода.

                                  А зачем метаться по десятку классов? Методы расширения не требуют знать реализацию расширямого контракта. Реализация вообще никак не связана с методами расширения. У самого метода расширения зависимость ровно 1 (одна) — расширяемый контракт.


                                  То есть, раньше я зависел от одного большого контракта на 60 методов, теперь завишу от 20 малых (по методу в каждом). А больше 9 зависимостей для класса — зло.

                                  С учетом того, что метод расширения не имеет состояния и зависит только от расширяемого контракта, он вообще не является зависимостью в терминах DI. А пытаться делать вид, что зависимость от контракта с 60 методами проще — есть и более дешевые способы себе лгать.
                                  Вы еще скажите, что IEnumerable создает зависимости от всех методов расширения LINQ.


                                  1. Oxoron
                                    20.10.2016 09:11

                                    Профит несомненный — метод расширения имеет простейшие контракт и реализацию, по сравнению с парой интерфейс-класс. Никакого внедрения зависимостей, никакой подмены реализации — просто переиспользуемая процедура. Вся «поддержка» метода расширения сводится к «работает — не трогай». Зато поддержка «похудевшего» основного контракта становится заметно легче.

                                    Если метод поддерживается как «работает — не трогай», какая разница где его не трогать?

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

                                    Опять-таки, это сейчас метод не зависит не зависит от внутренней реализации. А если зависимость появится? Рефакторить, перенося кучу кода из класса в класс?

                                    Но в целом, вы во многом правы. Спасибо.


                                    1. Bonart
                                      23.10.2016 01:38
                                      +1

                                      Если метод поддерживается как «работает — не трогай», какая разница где его не трогать?

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


                                      Опять-таки, это сейчас метод не зависит не зависит от внутренней реализации. А если зависимость появится? Рефакторить, перенося кучу кода из класса в класс?

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


                                      1. Oxoron
                                        23.10.2016 12:12

                                        Большая разница. Метод расширения опирается только на публичный контракт, метод контракта — на детали каждой конкретной реализации. Во втором случае «не трогать» не получится даже если не надо менять ничего старого, а достаточно добавить новую реализацию для контракта.

                                        Разумно, о таком сценарии я не думал. Хотя вы опираетесь на сценарий с необходимостью дополнительной реализации, вероятность которой не всегда высока.

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

                                        Вот тут ошибка. Есть сценарий: основной контракт, и потенциальное расширение. Один контракт — одна реализация (тесты не считаю). Предположим, мы добавили кеш в основную реализацию. Соответственно, юзание кеша ускоряет методы. Кеш приватный, содержит специфические Func<...> экземпляры.
                                        Тут у нас 2 варианта.
                                        1. Мы вынесли «потенциальное расширение» в методы расширения. После появления кеша у нас выбор: либо переносить все методы обратно (и менять контракты), либо оставить методы как есть, но тормознутыми.
                                        2. Мы не выносили «потенциальное расширение». После появления кеша мы на него спокойно переключаемся, получаем профит в скорости без изменения интерфейсов.
                                        3. Вариант 3: сделать кеш публичным (или внутренним) — нарушение инкапсуляции.

                                        Если же откинуть данный пример — получается, что чем вероятнее изменения в контракте, тем больше смысла не выносить методы в расширения. А если у нас одна «боевая» реализация на контракт — смысл выделения методов расширения еще меньше.


                                        1. Bonart
                                          27.10.2016 16:07

                                          Разумно, о таком сценарии я не думал. Хотя вы опираетесь на сценарий с необходимостью дополнительной реализации, вероятность которой не всегда высока.

                                          Я вижу три сценария, при которых затрагивается метод контракта:


                                          1. Изменение реализации (самый частый случай)
                                          2. Добавление реализации (реже, но при TDD — распространенная практика)
                                          3. Изменение контракта (самый редкий случай)

                                          На метод расширения действует только третий сценарий.


                                          Есть сценарий: основной контракт, и потенциальное расширение. Один контракт — одна реализация (тесты не считаю). Предположим, мы добавили кеш в основную реализацию. Соответственно, юзание кеша ускоряет методы. Кеш приватный, содержит специфические Func<...> экземпляры.
                                          Тут у нас 2 варианта.
                                          1. Мы вынесли «потенциальное расширение» в методы расширения. После появления кеша у нас выбор: либо переносить все методы обратно (и менять контракты), либо оставить методы как есть, но тормознутыми.

                                          Откуда берется "тормознутость" для методов расширения при условии неизменности контракта?


                              1. Oxoron
                                19.10.2016 15:36

                                Это не проблема, а ограничение: если методу нужны закрытые данные — он не может быть методом расширения.

                                А в чем проблема? Nested static class — и все закрытые члены к вашим услугам.


              1. Oxoron
                18.10.2016 15:33

                — Есть DTO и прочие классы для обмена данными, регулярно (де)сериализуемые и содержащие десятки полей.
                — Такое предпочитаю разбивать.

                Окей, есть класс
                Такие вроде в Magento любят
                public class DTO
                {
                    [XmlElement]     public string Value1{get; set;}
                    [XmlElement]     public string Value2{get; set;}
                    [XmlElement]     public string Value3{get; set;}
                    [XmlElement]     public string Value4{get; set;}
                    [XmlElement]     public string Value5{get; set;}
                    [XmlElement]     public string Value6{get; set;}
                    [XmlElement]     public string Value7{get; set;}
                    [XmlElement]     public string Value8{get; set;}
                    ...
                    [XmlElement]     public string Value80{get;set;}
                }
                


                1. Bonart
                  18.10.2016 15:36

                  Такой класс предпочту порождать кодогенерацией.


                  1. Oxoron
                    18.10.2016 15:43

                    Само собой. Но половину классов Magento после кодогенерации надо тюнить руками. После тюнинга остается 75 полей. И тут выбор — либо тюнинг выносить в кодогенератор (40 полей изменять), либо после первичной генерации изменять его только руками.

                    В любом случае в итоге либо здоровенный DTO, либо здоровенный кодогенератор. И перед нами снова вопрос: как это разделять? И вообще, стоит ли?


                    1. Bonart
                      18.10.2016 16:59

                      Но половину классов Magento после кодогенерации надо тюнить руками

                      Partial классы?


                      либо здоровенный кодогенератор

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


                      1. Oxoron
                        18.10.2016 17:25

                        Окей, будем считать у нас есть шаблонизатор.
                        Вопрос: что мы выигрываем за счет partial? Был один файл с DTO классом, стало 10. Навигация усложнилась.


              1. Oxoron
                18.10.2016 15:38

                — Особенно при поддержке какой-либо числодробилки, с алгоритмами Кнута-Матиясевича-Хаммурапи-и-еще-1000-людей. Особенно если вы реализуете нечто оптимальное на указателях. Особенно если вы вставляете какой-нибудь грязный хак перед дедлайном.
                — Все это входит в «почти».


                Все это входит в разделы «Оптимизация», «Грязные хаки», «Костыли», и, самое главное, «Cложная предметная область». Немного многовато для «почти», не находите?


                1. Bonart
                  18.10.2016 15:43

                  Нет, не нахожу. Ибо с кодом, состоящим из «Оптимизация», «Грязные хаки», «Костыли» со ссылкой на «Cложная предметная область» поработал изрядно и всякий раз оказывалось, что большая часть рефакторилась в простое и понятное.


                  1. Oxoron
                    18.10.2016 15:50

                    Шикарно.
                    Мы таки подошли к главному: какая часть не рефакторилась в простое и понятное? Было ли у этих кодовых кусков что-то общее?


                    1. Bonart
                      18.10.2016 17:00
                      +1

                      Они явно не были простыми и как следствие, несколько выбивались из темы статьи.


                      1. Oxoron
                        18.10.2016 17:29

                        А было ли у этих кусков какое-то более формализуемое общее? В каких именно реализациях нужны таки комментарии?


  1. Refridgerator
    18.10.2016 05:47
    +9

    Согласно первому критерию, их оптимальное количество должно быть 3, максимум 9, а у вас их аж 11. Противоречие.


    1. Bonart
      18.10.2016 08:28
      -2

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


  1. Tenqz
    18.10.2016 07:50
    -1

    Какая шикарная статья, но жаль что нет визуализации. Хотелось бы картинок в стиле Хорошо/плохо.


    1. nckma
      18.10.2016 08:09

      К «хорошо» / «плохо» нужно еще добавить «модно».
      А то я помню сперва всех заставляли венгерскую нотацию и комментирование кода, а теперь все это запрещают.


      1. Bonart
        18.10.2016 08:30

        "Модно" — ерунда, тех же венгерских нотаций две разновидности и та из них, что для приложений, вполне хороша во все времена.


  1. Refridgerator
    18.10.2016 08:15

    В промышленном программировании приоритеты стоят несколько по-другому:
    1. Надёжность и отказоустойчивость;
    2. Время доступа к данным;
    3. Время на изменение/корректировку алгоритмов.

    Кроме того, сложность системы определяет на количество объектов, а количество связей между ними. Гирлянда из 10 000 лампочек ничуть не сложнее гирлянды из 10 лампочек.


    1. Bonart
      18.10.2016 08:23

      Вы приводите критерии другого уровня — внешнее качество. А я о том, что позволяет добиться такого качества максимально дешево.
      Гирлянда из 10 тысяч лампочек соответствует коллекции из 10 тысяч элементов, а не классу с 10 тысячами свойств.


      1. Refridgerator
        18.10.2016 08:44

        Вы говорите об этом прямым текстом

        Особенности мозга человека таковы, что он плохо хранит и отличает более 7-9 элементов в одном списке при оптимальном их количестве 1-3.
        и качество и количество никак не упоминаете и не разделяете.


        1. Bonart
          18.10.2016 08:51

          Неужели вы помните все 10 тысяч лампочек из гирлянды?
          Или это все таки больше похоже на Гирлянда<Лампочка> { Количество: 10000 } ?


          1. Refridgerator
            18.10.2016 09:19

            1. Да (точнее, не я лично, а электрик).
            2. Похоже это может быть на что угодно. Сама система и описание этой системы — несколько разные вещи.


            1. Bonart
              18.10.2016 09:40
              +1

              1. Это противоречит вашим словам про одинаковую сложность гирлянд разной длины. По крайней мере для электрика.
              2. Так программисты имеют дело как раз с описанием системы на языке программирования.


              1. Refridgerator
                18.10.2016 10:04

                1. Не вижу противоречия. Исходя из вашего же определения, которое я уже процитировал, согласно которому сложность вы определяете от количества элементов, способных к хранению и различению в мозгу человека.
                2. Ну да. Но всё же нужно разделять сложность системы и сложность описания системы.


                1. Bonart
                  18.10.2016 11:08

                  способных к хранению и различению в мозгу человека.

                  … в виде линейного списка. Даже телефонные номера из 10 цифр люди запоминают, разбивая их на группы (обычно 3-3-2-2).
                  Вы у гирлянды тоже помните размер, а не каждую лампочку. И "электрик" у программистов тоже есть — это компьютер.


                  Ну да. Но всё же нужно разделять сложность системы и сложность описания системы.

                  И моя статья как раз про сложность описания.
                  PS: Человек может помнить на много порядков больше информации, но вся эта память — ассоциативная. Поэтому гирлянда на 10 тысяч лампочек будет экземпляров типа с двумя свойствами, а не с 10 тысячами. А тип с 10 тысячами свойств будет мусором, с которым нереально работать как с целым.


                  1. Refridgerator
                    18.10.2016 12:35

                    Вот об этом я говорю. Вы упорно рассматриваете задачу исключительно с точки зрения описания, вне контекста самой системы и управления.


  1. qadmium
    18.10.2016 12:41
    +2

    Статический метод — полноценный класс без состояния, все плюшки от неизменяемых объектов плюс меньший трафик памяти.


    Раскройте пожалуйста это утверждение, мысль где-то далеко и я за ней не поспел


    1. Bonart
      18.10.2016 14:35
      +1

      Есть такая точка зрения, что DTO, объекты-значения, неизменяемые объекты и т.п. не являются полноценными и не должны использоваться true-ООП программистами.
      Я полагаю ее в корне неверной и считаю процедурное программирование частным случаем объектно-ориентированного. Если вам не нужны данные экземпляра, то это не делает ваш статическим метод неполноценным.
      Да, у него нет класса-владельца, он сам себе является таковым. Просто у этой разновидности классов есть ограничение — отсутствие состояния.


      1. qadmium
        18.10.2016 14:45

        Есть такая точка зрения, что DTO, объекты-значения, неизменяемые объекты и т.п. не являются полноценными и не должны использоваться true-ООП программистами.


        видели, знаем :D

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


        т.е с этой точки зрения static это такой синтаксический сахар чтобы не писать new и не протягивать везде обьект?


  1. s-kozlov
    18.10.2016 12:53
    -1

    Комментарии в контракте — почти всегда полезны, комментарии в коде реализации почти всегда вредны.


    Комментарии в хорошем коде практически всегда вредны, особенно в контракте (если речь не идет о публичном API).


    1. Bonart
      18.10.2016 14:29
      +1

      Комментарии в хорошем коде практически всегда вредны, особенно в контракте

      Выбрасываемые исключения надо определять телепатией?
      Возможность использования в многопоточке — ей же?
      Влияние одних вызовов на результаты других — ясновидением?
      Любые ограничения, кроме тех, что ловит компилятор — вне закона?
      Расскажите, как понять без комментария, что IEnumerable не гарантирует повторную итерацию?


      1. s-kozlov
        18.10.2016 16:26

        Выбрасываемые исключения надо определять телепатией?


        Список throws уже отменили?

        Возможность использования в многопоточке — ей же?


        Возможность использования чего в многопоточке? Кастомной структуры данных? Думаю, если она thread-safe, т.е. тормозная, это полезно указать.

        Влияние одних вызовов на результаты других — ясновидением?


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

        Любые ограничения, кроме тех, что ловит компилятор — вне закона?


        Во-первых, что это за исключения, которые ловит компилятор? Это вы так называете checked exceptions? Если да, то они-то тут при чем?

        Расскажите, как понять без комментария, что IEnumerable не гарантирует повторную итерацию?


        Никак


        1. Bonart
          18.10.2016 17:06

          Список throws уже отменили?

          В дотнете и не применяли. Да и в яве у списка слишком неприятные побочки.


          Возможность использования чего в многопоточке? Кастомной структуры данных? Думаю, если она thread-safe, т.е. тормозная, это полезно указать.

          Thread-safe необязательно тормозная, но обязательно thread-safe. Факт о thread-(un)safe желательно знать у же при первом взгляде на интерфейс.


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

          Методы помещения значения в очередь и извлечения их оттуда.


          Во-первых, что это за исключения, которые ловит компилятор? Это вы так называете checked exceptions? Если да, то они-то тут при чем?

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


          Никак

          Вот вы и сами дали ответ о полезности комментариев в контракте.


          1. s-kozlov
            19.10.2016 06:29

            Да и в яве у списка слишком неприятные побочки


            Интересно даже. Какие?

            Методы помещения значения в очередь и извлечения их оттуда.


            Жесть какая-то. Неужели из названия интерфейса «Queue» и методов «void push(E elem)», «E pull()» не очевидно, как это работает?

            По остальному и всему сразу еще раз:

            Комментарии в хорошем коде практически всегда вредны, особенно в контракте (если речь не идет о публичном API).


            Выражение «практически всегда» означает, что могут быть исключения, но их мало. «Публичный API» значит, что IEnumerable — не очень подходящий пример. Про вред подробнее читать «Чистый код» Р.С.Мартина


            1. Bonart
              19.10.2016 13:32

              Интересно даже. Какие?

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


              Жесть какая-то. Неужели из названия интерфейса «Queue» и методов «void push(E elem)», «E pull()» не очевидно, как это работает?

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


              «Публичный API» значит, что IEnumerable — не очень подходящий пример.

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


              1. s-kozlov
                19.10.2016 16:59
                +1

                Да и в яве у списка слишком неприятные побочки


                Интересно даже. Какие?


                Недостатки у явовских контролируемых исключений те же


                Вы считаете, что в списке throws в Java можно объявлять только checked exceptions?

                В том или ином вашем собственном контракте влияние вполне может быть совсем не очевидным для других разработчиков.


                В этих редких случаях комментарии не помешают. Вот только этого надо по возможности избегать.

                а коллеги внутри компании должны проявлять телепатические способности и ясновидение?


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

                Вы уже таки прочитали главу Мартина про комментарии? Мне уже начинает надоедать ее пересказывать.


                1. third112
                  19.10.2016 18:12
                  +1

                  Вы уже таки прочитали главу Мартина про комментарии? Мне уже начинает надоедать ее пересказывать.
                  Да. На С.79 прочел, что комментарии — неизбежное зло. И далее, что совсем без них не обойтись. Дальше предлагаются примеры типовых случаев хороших и плохих комментариев. Формулируются правила, нпр.:
                  Короткие функции не нуждаются в долгих описаниях.
                  В такой осторожной формулировке с этим правилом мало кто не согласится. Тем более, что через несколько страниц (С.100) автор приводит исключение из этого правила: тело функции determineIterationLimit содержит всего две строки кода и целых три строки необходимых, по мнению автора, комментариев. Хорошо, интересно и убедительно написано. ИМХО нужно писать полезные комментарии и не писать вредные. Ok.


                  1. s-kozlov
                    20.10.2016 06:10

                    Ну вот, собственно, об этом я и говорил.


                1. Bonart
                  19.10.2016 18:30

                  Вы считаете, что в списке throws в Java можно объявлять только checked exceptions?

                  Понятия не имею — как нетрудно догадаться из моего профиля и списка хабов в статье я на яве не программирую. Вот только ценность такого списка у неконтролируемых исключений все та же: если компилятор не контролирует — тогда это де-факто комментарий.


                  В этих редких случаях комментарии не помешают. Вот только этого надо по возможности избегать.

                  Не такие уж и редкие случаи, если под них даже специальный язык был запилен (Eiffel). А для дотнета есть целая библиотека CodeContracts. Она не удалась, но ее проблемы были не в редксти случаев, а в производительности сборки.


                  Коллеги внутри компании должны создавать нормальные интерфейсы, которые не нуждаются в комментариях.

                  IEnumerable — нормальный интерфейс?


                  А еще внутренний код часто меняется

                  Часто меняющиеся внутренние интерфейсы без комментариев? Это как же надо ненавидеть собственных коллег.


                  1. s-kozlov
                    20.10.2016 06:14

                    Вот вы опять приводите частные примеры, которые в нормальном коде должны быть исключением, а не правилом.

                    Часто меняющиеся внутренние интерфейсы без комментариев? Это как же надо ненавидеть собственных коллег.


                    Изменения требований? Рефакторинг? Не, не слышал.


  1. third112
    18.10.2016 14:06

    Уважаемый автор,

    Комментарии в контракте — почти всегда полезны, комментарии в коде реализации почти всегда вредны.

    но ранее Вы писали:
    4 Комментарии
    Первая строка комментария перед объявлениями процедур, классов и т.п. должна давать понять их назначение. Последующие строки описывают те или иные особенности реализации.
    Комментарии к виртуальным методам должны описывать обстоятельства вызова, а не реализацию — она может быть перекрыта в наследниках.
    Комментарии в теле процедур и методов не должны описывать, ЧТО делает тот или иной оператор или блок, а должны указывать ЦЕЛЬ, для которой он (оператор) используется.

    Цели меняются гораздо реже, чем средства ее реализации
    Понять что делает код можно из него самого, понять ЗАЧЕМ по коду бывает нереально.

    Пересмотрели свое отношения к комментариям? или я чего-то не понял?


    1. Bonart
      18.10.2016 14:24

      Первая строка комментария перед объявлениями процедур, классов и т.п. должна давать понять их назначение.

      Это комментарий контракта


      Комментарии в теле процедур и методов не должны описывать, ЧТО делает тот или иной оператор или блок, а должны указывать ЦЕЛЬ, для которой он (оператор) используется.
      Цели меняются гораздо реже, чем средства ее реализации
      Понять что делает код можно из него самого, понять ЗАЧЕМ по коду бывает нереально.

      А это — то самое "почти" для комментариев в коде.


      Пересмотрели свое отношения к комментариям? или я чего-то не понял?

      Просто в данной статье про комментарии ровно одна строка. И это неудивительно — в коде комментарии разъясняют сложные места, а статья о том, что я считаю простым.


      1. third112
        18.10.2016 14:34

        ИМХО не только сложные, но и неочевидные места. Тривиальный пример. Переменную Х делим на переменную Y при этом нет проверки, что Y не равно нулю. Не очевидно: м.б. кодер забыл поставить проверку, а м.б. Y не равно нулю по условию подзадачи, проверялось ранее и т.д. Комментарий-пояснение ИМХО и в таком тривиальном случае необходим.


        1. Bonart
          18.10.2016 14:36

          Очевидно, что в случае равенства делимого нулю будет исключение. Этого в данном случае достаточно.


          1. third112
            18.10.2016 14:38

            М.б. нужен будет обработчик этого исключения?


            1. Bonart
              18.10.2016 14:45

              Может быть, но не факт. Более того, в этом месте кода мы вообще можем не знать, как должны обрабатываться исключения отсюда. И если добавлять комментарий, то за ним придется следить и актуализировать, даже если код рядом не меняется. Это чистый убыток.


              1. third112
                18.10.2016 15:01

                Вы сказали:

                Особенности мозга человека таковы, что он плохо хранит и отличает более 7-9 элементов в одном списке при оптимальном их количестве 1-3.
                А неочевидных мест в коде м.б. тысячи. Помнить все невозможно. А если над кодом работает команда, то только один знает почему нет проверки на ноль и обработки исключения.
                Все может быть!
                Быть может, вы умрете,
                Вас выгонят,
                Сгорите на работе,
                Или на базе вас задавит свеклой в таре…
                Товарищи! Пишите комментарий!


                — Леонид Бунич


                1. Bonart
                  19.10.2016 13:17

                  А если над кодом работает команда, то только один знает почему нет проверки на ноль и обработки исключения.

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


                  1. third112
                    19.10.2016 14:16
                    +1

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

                    м.б. Y не равно нулю по условию подзадачи

                    Если посмотреть достаточно большой нетривиальный код, то там таких случаев м.б. довольно много при их большом разнообразии. Если метод реализует «в лоб» широко известный алгоритм, то достаточно комментария типа "// Алгоритм Флойда — Уоршелла". Но если в коде используется какие-то свойства матрицы расстояний, специфичные для данной задачи, то эти места кода нуждаются в комментариях. Часто бывает, что применяется оригинальный алгоритм, сделанный специально под данную задачу. Тут одних комментариев может оказаться мало и нужны будут доказательство корректности алгоритма и оценка теоретической сложности. Это уже отдельная документация, сопровождающая код. А в комментариях должна быть ссылка на соответствующий файл документации. Если алгоритм эвристический, то это нужно написать в комментарии и т.д. Отсутствие подобных пояснений превращает код в ребус и не способствует простоте. ИМХО это очевидные вещи, и мне немного странно, что мы о них спорим.


                    1. Bonart
                      19.10.2016 14:41

                      Но если в коде используется какие-то свойства матрицы расстояний, специфичные для данной задачи, то эти места кода нуждаются в комментариях.

                      Это и есть сложные места по определению.
                      Т.е. мы вернулись к тому, с чего начали — комментарии в коде полезны, когда объясняют, зачем нужна та или иная сложность. Так что никто вас специально ни на чем не ловил.


                      1. third112
                        19.10.2016 14:51

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


  1. third112
    18.10.2016 15:00

    ошибка ввода — прошу извинить


  1. Taller
    18.10.2016 17:52
    +1

    Ну, вот, вы превысили количество пунктов в статье.

    Кстати, вместо 7-9, обычно говорять о 7 ± 2, т.е. от 5 до 9, также говорят, что элементы должны быть однородны, а также восприятие должно быть одновременным. С интерфейсах, параметрах и т.п. можно проводить последовательный анализ.


    1. Bonart
      18.10.2016 17:54

      С интерфейсах, параметрах и т.п. можно проводить последовательный анализ.

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


      1. Taller
        18.10.2016 18:03
        +1

        Нужны все, это верно. Не все сразу, это тоже верно.
        Сразу — это одновременно, в один момент. А операторы записываются слева направо, сверху вниз, не в один момент.

        Если найду ссылку — будет пруф.


  1. overmes
    19.10.2016 11:12
    +1

    Для меня самое сложное, когда я просматриваю чужой код, это:

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


    1. Bonart
      19.10.2016 13:14

      не логичность последовательности шагов в алгоритме

      Вы можете выделить критерии логичности-нелогичности? Вполне возможно, что для автора кода они иные.


      когда алгоритм необоснованно перегружен лишними шагами и данными

      Каковы ваши критерии обоснованности в данном вопросе? Скорее всего, автор кода уверен в обратном.


      1. overmes
        19.10.2016 13:31

        Я согласен что многие пункты субъективны.

        Вы можете выделить критерии логичности-нелогичности? Вполне возможно, что для автора кода они иные.

        Мы обычно одеваем сначала кофту, потом куртку, а не кофту под куртку. Программно мы можем и так и так делать.

        Каковы ваши критерии обоснованности в данном вопросе? Скорее всего, автор кода уверен в обратном.

        Это про KISS. Я часто проверяю код новичков и там автор кода мало в чем уверен.
        Автор должен свою уверенность и принятые решения как-то выражать в коде.


        1. Bonart
          20.10.2016 01:14
          +1

          Я согласен что многие пункты субъективны.

          Нередко под ними скрывается вполне объективный критерий, который сможете воспроизвести не только вы, но и ваши коллеги. Не факт, что они с ним согласятся, но по крайней мере смогут заранее понять, почему то или иное решение будет вам симпатично или наоборот.


      1. overmes
        19.10.2016 13:40

        Я сам подумывал об выступлении\статье на эту тему. И основной посыл хотел сделать в том, что сложность это не только как разделены классы, а и то что один и тот же алгоритм можно очень по разному написать.

        Я пока не видел(но я не много где смотрел) ничего про эту мою так называемую логичность последовательности шагов.

        Между двумя точками мы идем по кратчайшему расстоянию от начала до конца, но программно мы можем начать идти с середины до конца, а потом пройти от начала до середины.


        1. third112
          19.10.2016 14:32

          один и тот же алгоритм можно очень по разному написать
          Вы про описание алгоритма или про его реализацию на конкретном языке программирования? В литературе по CS (и в той же википедии) алгоритмы часто описывают на естественном языке (русский, английский и т.д. + мат. формулы) или на псевдокоде. Еще один способ — это блок-схемы. Конечно же, и описание и реализация могут быть сделаны очень по разному.


          1. overmes
            19.10.2016 14:34

            Про реализацию. Я не про CS алгоритмы, а про обычную бизнес логику.


  1. third112
    19.10.2016 15:01

    Интересный вопрос о соотношении предложенных критериев с подходами software metric. Кроме примитивного подсчета числа строк были предложены и очень сложные.


  1. Refridgerator
    20.10.2016 07:20

    Ещё вспомнилось:

    «У каждой задачи есть очевидное, простое и неправильное решение» © А.Блох


    1. overmes
      23.10.2016 11:30
      +1

      «Есть два подхода к программированию. Первый — сделать программу настолько простой, чтобы в ней очевидно не было ошибок. А второй — сделать её настолько сложной, чтобы в ней не было очевидных ошибок.»
      Tony Hoare. Профессор, занимался реализацией Алгол 60, сейчас исследователь в Microsoft Research.