Пользователь
0,0
рейтинг
20 декабря 2013 в 10:21

Разработка → Утрата слабой связанности из песочницы

Перевод статьи LOSING LOOSE COUPLING

монстр-трак велосипед Когда меня просят присутствовать на собеседованиях, я обычно задаю один вопрос кандидату: «Что такое хороший код?». Тревожит, что часто можно услышать от недавних выпускников: «Наличие хороших комментариев». Это неправильный ответ. Кто учит их этому? Пугающе. Но я отвлекся… Не думаю, что есть правильный ответ на мой вопрос, однако я бы принял что-нибудь вроде «сильное сцепление (high cohesion) и слабая связанность (loose coupling)». По крайней мере это что-то говорит о коде. Но если это собеседование Java разработчика, я не дам бедняге уйти без нескольких дополнительных вопросов. Потому что Java разработчики полностью обезумели. Они одержимы желанием порубить код на супер-пупер мелкие кусочки. Мы рубим и рубим до тех пор, пока практически ничего не останется. Как только маленькие дорогуши разделены мы начинаем беспокоиться о том, чтобы они не трогали друг друга. Ох, малышки! Мы должны защитить их друг от друга любой ценой. Каждый маленький кусок кода получает свой собственный интерфейс, чтобы он не мог замарать свои руки дотянувшись до других частей напрямую. Мы связываем их магическими фреймворками. Которые используют абстрактные прокси, создающие фабрики и так далее.

Представьте велосипед, сделанный по таким принципам. Рама порублена на кусочки, длиной 1 сантиметр, соединенных по типу позвоночника. Будет ли она более гибкой? Определенно — да. Будет ли она практичной? Конечно, нет. Она будет дороже в производстве в сотни раз. Она также будет ломаться в сотни раз чаще. Такой велосипед приведет к большему количеству несчастных случаев, и не в последнюю очередь, будет странно выглядеть и на нем будет трудно ездить. Наша спина должна быть гибкой, поэтому позвонки имеют смысл. Велосипеды — нет.

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

Еще одна вещь, которую мы делаем при разработке велосипеда — это разработка велосипеда, а не обобщенного (generic) «устройства транспортировки». Мы не пудрим мозг нашим клиентам, о том, что однажды они смогут пересечь на нем Атлантический океан. Если они захотят сделать это, то уж точно не на нашем велосипеде. Если наши клиенты хотят взлететь на крышу небоскреба, мы им мало чем поможем. Это нормально. Мы хотим сделать лучший в мире ВЕЛОСИПЕД. Велосипед, который может быть переконфигурирован в любое транспортное средство и все еще будет хорошо работать может быть опцией для Бэтмена, но, к сожалению, многие из нас не обладают такими бюджетами. Мы хотим, чтобы наши системы были крутыми, соответствовали своему назначению, и мы хотим знать, что мы строим. Гибкость и конфигурируемость для людей, которые не знают, чего хотят, или когда вы хотите, чтобы сторонние поставщики разрабатывали части системы. Производители велосипедных рам точно не ограничивают свои велосипеды только одним типом сиденья, одним типом руля или одним типом колес. В терминах программирования они соединяются с интерфейсом Руль, который может быть реализован любым количеством конкретных моделей рулей. Но рама — это их собственный продукт, который не рубится на маленькие заменяемые фрагменты. Если они захотят делать различные рамы — догадайтесь, что? Они сделают разные модели. Модели используют много общих черт, но они все-таки полностью разные.

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

Если мы захотим, чтобы у нашего велосипеда были колеса от монстр-трака, необходимо будет изменить наш дизайн и сделать что-то, чтобы они подходили. И вы знаете что — мы можем это сделать в программировании. Я пришел к этой удивительной технике, не знаю, можно ли сделать то же самое в IntelliJ, но по крайней мере в Eclipse (которым я пользуюсь) вы можете выбрать код, который нуждается в изменении: удерживая клавишу SHIFT нажимая на кнопки курсора до тех пор, пока часть кода, которую надо изменить не будет подсвечена; отпустить клавишу SHIFT и нажать DELETE. И код исчезнет! И тогда вы можете написать что-нибудь другое. Это действительно работает! Я пробовал. В коде Java тоже, не только в xml или properties файлах! Это поразительно!

Когда-нибудь замечали, как большие корпоративные проекты стоят целое состояние, занимают вечность в разработке и потом эффектно проваливаются при попытке получить результат? Существует много причин для этого. Но я убежден одно из них — это гибкость. Гибкость тесака и вакуумного пакета, индивидуально упакованные в мини кусочки обобщенного (generic) кода. Вместо создания (возможно нескольких) систем, которые хороши в специфичных известных случаях мы настаиваем на создании ЕДИНОЙ (супер конфигурируемой) СИСТЕМЫ КОТОРАЯ ВСЕМ УПРАВЛЯЕТ. Программа, которая будет работать для любого, везде, не важно какие у них потребности.

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

Я за слабую связанность, но сделайте одолжение: убедитесь, что ваше решение связано с вашей проблемой.
@pestell
карма
9,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

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

  • +8
    Интересно, как будет выглядеть велосипедная рама, если проектировщик ожидает (и по опыту уверен) что любая самая неожиданная деталь этой рамы может измениться во время проектировки или, что бывает гораздо чаще, непосредственного производства. И без учёта времени на доработку и бюджета.
    • +25
      Программисты очень часто занимаются производством велосипедов, которые, возможно, полетят в космос, а, возможно, отправятся исследовать дно океана.
      • +1
        Только потому, что заказчик, как правило, не знает, чего хочет.
        Вот тут-то и нужен хороший менеджер.
        • +4
          По аналогии с велосипедами — «я бы хотел, провести исследование и для 10% покупателей поставить еще один руль вместо сиденья. А в чём проблема? У вас же уже есть руль? Ну так используйте его повторно!»
        • –2
          Удивлен, что комментарий в плюсе, т.к. именно под таким соусом подается «водопад».
        • 0
          Нет, потому что сам боится потом «все переделать с нуля». Поэтому с самого начала старается заложить абстрактность мега-уровня.
      • +4
        На моей памяти еще ни один велосипед не побывал в космосе и ни один не отправился в глубины океана. А если в какой-то момент стало необходимо поменять размер колес — быстрее и дешевле поменять и раму.

        Отличный коэн на тему слабой связности — thecodelesscode.com/case/119?lang=ru
  • +26
    На лицо попытка доказательства по аналогии с объектом реального мира. По умолчанию все такие аналогии надо считать лживыми.

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

    Мысли правильные, аргументация — неочень.
    • +15
      Все аналогии лживы, ибо апеллируют к эмоциям, а не к разуму.
    • 0
      Обобщения используются, вообще говоря, не только для реюза общей части, а для последующей более легкой поддержки ОДНОГО общего фрагмента вместо 15 копий. Поэтому у обобщения есть разумные пределы, которые можно увидеть двумя способами: либо знать развитие проекта наперед, либо иметь опыт непредсказуемого развития и его обычных последствий.
  • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    Актуальная статья. Иногда кажется, что некоторые программисты пишут код ради самого кода.
  • +8
    Всегда умиляла любовь людей из мира CS/IT к аналогиям и метафорам. Если забыл умные слова или не можешь привести конкретный пример, сделай мудрое лицо и расскажи про яблочки велосипеды.
    • 0
      Есть обратная сторона медали.
      Если ты четко понимаешь принцип работы некоторой системы или явления, то должен быть способен объяснить «на пальцах» как это работает.

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

      PS: я понимаю, что в данном треде шло обсуждение попытки внушить правдивость одного утверждения путем прикрытия другим, более простым и, возможно, не связанным напрямую с первым.
      • +2
        Квантовую физику можно объяснить с помощью бытовых аналогий?
        • 0
          Ну про кота же сочинили вполне приземленную историю.

          А вот и про увлечение пространства во вращение вблизи массивного вращающегося тела.
          image
          • 0
            Ну, это всё научпоп, попробуйте, например, объяснить волновую функцию с помощью бытовых аналогий. Да и вообще всё в квантовой физике математические абстракции, у которых бытовых аналогий то и нет
            • 0
              Ну понятно, что специфичные детали трудно объяснить на пальцах. Я же о принципах работы, об основах процессов и явлений говорил.
    • 0
      Метафоры здесь, я думаю, лишь для более выразительного изложения, а суть выражена в последнем предложении.
  • +6
    Плохой выбор метафоры.
    • 0
      А по-моему вполне наглядно, потому что есть рама, есть виртуальные узлы(руль, колёса) и есть абстрактные в (например багажник). Другое дело, что кто-то умудрится из рамы велосипеда в итоге сложить кузов машины попутно заткнув каретку картоном, а дырку рулевого стакана заклеив скотчем. Но это уже вопрос к клиенту — если нравится, пусть (велосипед же в итоге был куплен), но все претензии по итоговому качеству — самому себе же. Ну а кто-то возьёт напильник и приделает искуственную ногу к одной педали, потому что у конкретного человека только одна нога — и в итоге получит то что нужно.
  • +3
    Один мой знакомый считает, что когда программист начинает тратить много времени на архитектуру — это такая форма прокрастинации. Вроде бы и делом занят, даже полезным и интересным, но при этом конечный продукт стоит на мертвой точке.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +9
        Понимаю ваш сарказм. Оппа-говносайт-стайл — тоже не выход. Поэтому я и не сказал, что однозначно разделяю его точку зрения.

        • +1
          Поэтому совет в стиле КО: нужен баланс
      • 0
        Ну да, а потом другие ребята приходят и рефакторят то, что первые накодили. Всем кушать надо, все при деле. Межпрограммерский заговор налицо!
        • 0
          Зачем программировать вообще? Есть же давным давно написанный ИИ, который может сам программы писать. Главное, никому не показывать. Межпрограммерский заговор ведь :)
    • +4
      Это так. Но с другой стороны, два месяца программирования могут съэкономить один час проектирования архитектуры.
      • –1
        Ну тогда может за час все спроектировать и потратить два месяца без этого часа на что-то более полезное?
  • +2
    По существу вопроса. Насчет Java. Я лично программирую в основном на Scala и JavaScript. Мне нравится и Java, но мне кажется, проблема модели «сильное сцепление и слабая связанность» в том, что на Java она стоит слишком уж дорого по сегодняшним меркам. Очень много кода приходится писать, чтобы ее поддерживать. Опять же система зависимостей Maven/Ant(кстати, и SBT тоже) довольно громоздкая, и там довольно тяжело поддерживать большое количество микромодулей. Много обслуживать приходится. Скажем, если сравнивать с тем же NPM для Node.JS, то учитывая все плюсы и минусы Ноды, нельзя не признать, что на практике низкий порог для публикации модулей сыграл важную роль в развитии системы модулей. Системы из микромодулей со слабой связанностью на Node/NPM собирать гораздо легче, чем на том же Java.
  • +1
  • 0
    Для меня лично, всплывают ассоциации между GNU Hard и Linux
  • +2
    иногда это приводит к AbstractSingletonProxyFactoryBean
  • +2
    Как то вы слишком категоричны. Меня например не устраивает когда заднее колесо знает о руле. Мне нравится хороший дизайн. Дробить раму по сантиметру это конечно бред, объект должен быть целостным, функционально значимым.
    • +1
      > Меня например не устраивает когда заднее колесо знает о руле.

      Напрасно. Адаптивная подвеска велосипеда сделает более надёжным управление им на больших скоростях по бездорожью. Эволюция горных велосипедов всё же идёт к автоматическому учёту состояния рельефа местности и подстройки степени демпфирования заднего и переднего колёс. Очень важно, чтобы эта подстройка осуществлялась согласованно, и тогда будет возможно удерживать большую среднюю скорость на пересечённой местности, чем сейчас. Электромеханические системы пока что страдают излишним весом (из-за аккумулятора и механизации), поэтому задача по управлению подвеской целиком ложится на гонщика и на его способность в момент ускорения выключать специальным рычажком мешающую мягкость подвески.
    • +1
      Меня например не устраивает когда заднее колесо знает о руле.


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

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

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

    Я хочу сказать о том, что для каждого изменения в сторону развязывния кода нужны предпосылки. Считаю, что главной задачей разработчика в этой части является создание такого кода, который при необходимости несложно рефакторить и развязать. Хотя это конечно ИМХО.

    Модуляризовать ради того, чтобы просто получить кучу модулей — раньше времени увеличить сложность продукта. Создать кучу интерфейсов, применить кучу паттернов просто так? Сложность (а соответственно и стоимость разработки) должна возрастать с ростом полезности, ведь так?
    • 0
      У Java-прлограммистов всегда есть ответ на вопрос: нафига это выделено в отдельную функцию на три строки. Для тестирования! Понять, что код тестов и код продукта несопоставимы по важности им почему-то не дано. И вообще иногда складывается впечатление, что главная цель программирования — «покрыть» всё тестами. Почему, зачем, какова цель этого сакрального действия — неясно.

      Нет, я не против тестирования. И понимаю что иногда код таков, что написать тесты к нему не сложно и очень сложно. Но блин, когда в код добавляется сток 100 для того, чтобы тесты стали на 50 строк короче — это уже перебор.

      Тесты — это вспомогательный элемент. Как леса в строительстве или временные подпорки. В идеале — они вообще не должны влиять на архитектуру или, уж по крайней мере, влиять минимально. А не являться оправданием для того, что у вас всё работает через… я даже не знаю что.
      • 0
        Скажем так, тестирование конечно важно. Даже и необходимо. Но все жа главная цель программирования — написать программу, а не тесты. И эта программа должна правильно делать то, что она должна делать.

        Но тут возникает проблема — что считать правильным? Я для себя определил так: делать правильно — это делать сообразно требованиям осозанным мной и заказчиком в данный момент времени. Требования могут немного измениться в процессе разработки и надо закладывать такую гибкость, которая позволит недорого изменить функционал всвязи с этим. Ну а потом — рефакторинг. Вот тут будут незаменимы тесты как раз.
      • +1
        Тесты улучшают архитектуру. Если для программного кода очень сложно написать тест, то проблема, очевидно, в плохом программном коде и этот код нуждается в рефакторинге (с дроблением на кусочки и определением более понятных связей между ними).
        • 0
          Всё так же как и с велосипедом. Если нужно сделать велосипед и ездить на нем 50 метров до магазина — можно и монолитным сделать, а вот проф велосипеды должны быть хорошо протестированы с разными колёсами, каретками, шатунами и так далее по списку. Всё зависит от целей :) Если разрабатывается велосипед для поездки в магазин, нет смысла тратить x2-x3 времени на хорошую архитектуру и тесты. Имхо.
      • +2
        Фаулер писал, что наличие тестов — это необходимое условие для проведения рефакторинга. А рефакторинг, это единственный способ улучшения архитектуры уже написанного приложения в случае, если в этом появится необходимость.

        Даже если у нас не супер хорошо спроектированный код, который покрыт тестами, то, при возникновении необходимости, мы сможем его отрефакторить. А вот если тестов нет — не сможем, какой хороший код бы ни был.

        Отсюда простое следствие, если без изменений в архитектуре нельзя покрыть код тестами, значит это изменение в архитектуре сделать нужно, потому что если не покрыть код тестами… см. первый абзац…
        • –1
          Когда фаулер это писал, то еще не было автоматического рефакоринга. Сейчас есть инструменты, которые могут проводить рефакторинги, гарантируя корректность кода.
          • +1
            В завасимости от языка и конкретной среды разработки, набор гарантираванно безопасных рефакторингов разнится, и для таких языков как Java или C# далеко не полон, и не достаточен для серьезного рефакторинга. Это вызвано тем, что инструменты рефакторинга опраются на статически выводимую на основании анализа кода информацию, а есть ещё рантайм…

            Например, в сишарпе «безопасным» рефакторингом «Introduce factory method» можно легко сломать всю систему, т.к. инструмент рефакторинга не знает, что класс может инстанциироваться через DI фреймворк, которому нужен открытый конструктор (а рефакторинг делает его приватным).

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

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

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

                Пример решения вышеупомянутой проблемы:
                Вводится соглашение, что каждый класс, который допускает создание через контейнер — помечается аттрибутом. Пишется тест, который проверяет, что все классы, помеченные аттрибутом имеют публичный конструктор. Дополнительный бонус — разработчик знает, что если класс помечен аттрибутом, то выполнять рефакторинг «Introduce factory method» — возможно не стоит.
                • 0
                  Тогда можно и в инструменты встроиться, которые скажу что не надо рефакторить классы с атрибутом. И снова тесты не нужны.

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

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

                    В итоге тесты всё равно нужны, только не на саму программу, а на «встраивание в инструменты рефакторинга» и это замкнутый круг, разорвать который можно лобо тестированием, либо автоматическим доказательством корректности кода, что ещё более трудоемко…
                    • 0
                      Ну что за бред? В простых случаях статический анализ на порядок проще, чем тесты. Ибо тесты это динамика, эмуляция входных данных и зависимостей, а статический анализ работает не более чем с AST. Особенно проверки соглашений сделать в C#\Java совсем несложно. Для этого и тесты не нужны ибо код будет очень простой.

                      В итоге тесты всё равно нужны...

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

                      Тестирование на самом деле помогает очень мало.
                      • 0
                        Для тестов тоже нужны тесты ;-) Ибо наличие тестов не гарантирует корректности…

                        Если предположить, что в каждой строчке кода содержится ошибка с вероятностью 0,001, то в 100 строках кода хотябы одна ошибка будет с вероятностью 0,0952

                        Предположим, что мы написали 100 строк юнит тестов. Юнит тест содержит ошибку с той же вероятностью. Вероятность ложно-положительного юнит-теста (читай, что ошибка попадет в продакшен) в этом случае будет 0,00906304 (в 10 раз меньше).

                        Итого:
                        100 строк кода — 0,0952 вероятность ошибки в продакшене
                        100 строк кода + 100 строк тестов = 0,00906304 вероятность ошибки в продакшене
                        100 строк кода + 100 строк кода = 0,181 вероятность ошибки в продакшене

                        Не бейте тапками, ибо я предположил, что ошибки не всязаны между собой.
                        • 0
                          Кстати… очень интересно посмотреть зависимость этих чисел от длины кусочков кода. Становится понятно подсознательное желание побить код на как можно более мелкие независимо тестируемые кусочки…
                        • 0
                          Юнит тест содержит ошибку с той же вероятностью.

                          С хорошо тестируемой архитектурой вероятность будет меньше. Тупо не в чем ошибаться :)

                          Но в подсчетах вы, кажется, не учли, что ошибка попадет в продакшен, если совпадут ошибки и в коде, и в тесте. Грубо говоря, будут в одной строке. Только в этом случае тесты ложно-положительно сработают
                          • 0
                            Но в подсчетах вы, кажется, не учли, что ошибка попадет в продакшен, если совпадут ошибки и в коде, и в тесте.

                            Я учел. Это было сказано про ошибку в юнит тесте без учета его результата:
                            Юнит тест содержит ошибку с той же вероятностью.


                            С учетом результата у меня получилось вот это:
                            100 строк кода + 100 строк тестов = 0,00906304 вероятность ошибки в продакшене


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

                              Очень грубое предположение по-моему.
                              • 0
                                Очень грубое предположение по-моему.

                                Согласен, грубое. Но для приведенной грубой модели — вполне допустимое :-)
          • 0
            тесты нужны для проверки логики, чтобы компоненты/система вели себя по-прежнему, как и раньше (если требования не сменились), а не для корректности кода. О нарушении последнего вам расскажет компилятор. Ну и плюс полностью согласен с комментом habrahabr.ru/post/206684/#comment_7125298, прям в точку
            • 0
              Еще раз: современные инструменты позволяют много рефакторинги проводить не изменяя наблюдаемого поведения. То есть сохраняя корректность.
              • 0
                окей, а как вы достигнете необходимого поведения изначально?
                • 0
                  Отвечу вопросом на вопрос. Вы считаете что это невозможно?

                  Я вот умудрялся писать программы, которые делают то что надо, без тестов, я даже не знал о них. Иногда даже не запуская (тяжелые были времена, компьютер бы доступен раз в неделю). И это я еще в школе учился.
                  • 0
                    я хотел узнать, каким образом вы достигали требуемого поведения системы? что было критерием достижения цели?
                    • 0
                      Код соответствовал алгоритму, приводящему к решению задачи.
                      • 0
                        это по вашему мнению ответ на вопрос «каким образом»? ладно, пойдет, если не будете отвечать
                        • 0
                          Еще раз: есть алгоритм, на бумаге или в голове. Код соответствует алгоритму. Это же можно утверждать просто посмотрев на код.
                          • 0
                            ладно, бог с ним с этим. Вы первый человек из тех, кого я встретил, который сразу пишет корректный код без ошибок, самодокументируемый, по которому сразу понятен весь алгоритм, и при этом временнЫе затраты максимально снижены
                            • 0
                              Это вы уже сами придумали.
                              • 0
                                сделал выводы, что вы одним только взглядом сразу видите правилен код или нет, значит можете в уме его откомпилировать на предмет корректности поведения
                              • 0
                                предлагаю остаться при своих мнениях, и в будущем, быть может, кто-то из нас двоих, а может быть и оба, скажут себе «а тот чувак с хабра был прав» :)
                    • 0
                      Обычно это ручной прогон нескольких сценариев.
                      • 0
                        да, но это совсем другой вид тестов. Быть может я отхожу от общепринятой терминологии, но у меня подобные тесты закрепились под название функциональные. И порой прогнать целый такой тест утомительно и отнимает уйму времени. Особенно если речь идет о приложениях с графическим интерфейсом
                        • 0
                          Во многих случаях целиком их не прогоняют, а проверяют лишь только что написанный код.
                          • 0
                            разумеется. Но даже в таком случае это достаточно утомительное занятие и по идее вообще работа не разработчиков, а тестеров. Просто в описанном вами случае ответственность смазывается, а разработчик так вообще занят не своим делом, впридачу код пишется маленькими итерациями, каждый шаг необходимо проверять. А проверять запуском — откровенное расточительство временем.

                            Но, на самом деле. это тема холиварная :)
                            • 0
                              Ну, всё же дело разработчика (и его начальства) как ему проверять свой код — автотестами или ручными. И времени требует и то, и другое. А ответственность часто не размазывается, потому как тестировщиков как таковых и нет.
                              • 0
                                если тестировщиков нет, их обязанности берут на себя разработчики, но даже в таком случае данный этап располагается в самом конце очередной итерации разработки.

                                По поводу как проверять код — разумеется каждый склонен выбирать сам, нет какого-то уголовного кодекса по нарушению каких-то правил. Есть устоявшиеся, хорошие практики, подтвержденные и проверенные неоднократно и не вчера. Им можно следовать. Но никто не запрещает от них отходить. Только если такие отклонения идут вразрез с практиками, грешить на них точно не следует. Советую вам найти посты на хабре, посвященные тому же TDD. Там тема нашего с вами холивара развёрнута гораздо шире, глубже и большим кол-вом участников. Есть, кстати, даже целые притчи. Там уже определитесь, на чьей стороне вы, с кем согласны и с кем нет. Я свою позицию выбрал.
                                И нет тут единого мнения, которое устраивало бы всех.
                                • 0
                                  Про TDD я и сам могу посты писать, но частая проблема что автотесты запрещают использовать, в лучшем случае разрешают их писать в личное неоплаченное время.
                                  • 0
                                    так я не столько о постах говорил, сколько о комментариях к ним, к этому и употребил «большим кол-вом участников» и «холивар».
                                    Тесты как правило пишутся вперёд, поэтому о каком неоплачиваемом времени идёт речь — не совсем понимаю. В голове представляется картина, как разработчики прежде чем писать код, в нерабочее время проектируют систему и пишут по кейсам тесты. А в остальное время занимаются простым кодингом, с которым и обычный индус справится.
                                    Что касается упорства начальства, так тут проблема возможно даже и не в использовании методологии, а неумении «продать» её применение начальству. Во всяком случае никто не мешает заложить это время в общую разработку (собственно, это и есть часть разработки). И если уж сроки всегда и строго, даже с точностью до итерации, устанавливает начальство, то это уже рабство какое-то
                                    • 0
                                      В комментариях я тоже отмечался :)

                                      Ну, вечером дали задание на завтра, пришёл домой и тесты написал. Заодно поправил те, что за день сломались.

                                      Продавать я никогда не умел :( А со сроками сложно — чем за монитором занимаюсь никто не следит, но вот дифф за день виден же.
                  • 0
                    Вы мне напомнили об одном посте, который недавно проскакивал на хабре:
                    http://habrahabr.ru/post/203398/
        • 0
          Даже если у нас не супер хорошо спроектированный код, который покрыт тестами, то, при возникновении необходимости, мы сможем его отрефакторить. А вот если тестов нет — не сможем, какой хороший код бы ни был.
          Чушь это. Даже скажу больше: профанация. Для рефакторинга не нужны unittest'ы. Совсем. Нужны функциональное тесты проверяющие всю систему — и всё. Если они достаточно хороши, но что-то не покрывают, то у вас появляется готовый способ что-то отрефакторить: выкинуть этот кусок.

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

          Когда вспомогательный инструмент начинает вдруг определять архитектуру всей системы — получается бред. Это всё равно как если бы здания строили не исходя из того, что нужно получить, а исключительно исходя из того, чтобы к любой его части был бы идеальный подход с лесов, которые бы никогда не убирались. Хотели бы вы жить в таком здании?
          • +1
            Вы вступили на скользкий путь холивара на тему «что лучше, юнит тесты или функциональные тесты».
            Для себя я сам не решил, что лучше, и использую и то, и то, в зависимости от ситуации, но в своем комментарии я не говорил про какие-то конкретные виды тестов. Я говорил про необходимость тестов вообще. Потому не понимаю, почему вы, называя мое утверждение чушью, сами — фактически утверждаете то же самое.
      • 0
        То, о чём вы пишите мне знакомо, и соглашусь, что ломать архитектуру ради тестов как-то странно. И здесь мне кажется лишь проявляется ошибка в использовании TDD, то есть когда тесты пишутся на существующий код.
  • +5
    По сравнению создания программных продуктов с проектами в реальном мире есть хорошая метафора:
    «Почему постройка домов никогда не затягивается по срокам так, как создание программного обеспечения? Потому что после построения дома заказчик не может сказать: „А теперь я хочу, что бы лифт ездил не только между этажами, а еще и внутри этажа, развозя корреспонденцию по отделам“»
    В этом и основная проблема — в IT требования к продукту постоянно меняются, и хорошая архитектура должна давать возможность реализовывать эти требования без необходимости разбирать дом до кирпичика и строить всё заново.
    • 0
      А не задумывались почему так? Не раз встречался с фразами от потребителя: «Ну вы же специалист — ну что вам стоит?» Хотя в общем случае это правильно, что заказчих хочет чтобы лифт ездил и так и сяк. Хочет — надо сделать. Ведь софт (soft) — это мягкий, податливый. В отличие от харда (hardware) — который просто так и не изменить. И софт должен оставаться мягким вне зависимости от его сложности, его должно быть можно изменить так, как хочет заказчик. Это тоже одна из задач разработчика. Прослойка firmware — другое дело, кстати, она не сможет делать больше, чем заложено в железе.

      Если заказчик просит изменить что-то, надо просто назвать ему соответствующую цену. И все вопросы сразу отпадут ;)
      • 0
        Вы сильно упрощаете, такой механизм может работать только при заказной разработке.
        Если вы разрабатываете продукт, который продается разным заказчикам, то либо продукт сможет обслуживать требования 100 разных заказчиков и будет успешен, либо вы продадите первому заказчику, а остальных потеряете.
        • 0
          Не совсем понял к чему вы это написали? Я как раз говорил о том, что продукт должен быть достаточно гибким, чтобы у разработчиков была возможность подстроиться под требования рынка, но при этом достаточно жестким, чтобы не развалиться по пути к решению задачи.
  • +4
    Аналогия велосипеда и программного совершенно не корректна. Да железная рама должна быть прочной из единого куска алюминия. Но программный код не должен быть монолитным чтобы быть надежным
    • 0
      Ответил ниже. Но в данном опусе мне кажется не столь принципиально, на чем показывать аналогию, вся суть, на мой взгляд, выражена в последнем предложении. Но если бы я написал только его, это было бы очевидно.
  • 0
    Термин «велосипед» явно выбран неудачно, лучше бы машина или чтото похожее.
    • 0
      Возможно, так как в у нас «велосипед» имеет ещё одно значение, но я решил оставить переводом, а не статью «по-мотивам».
  • +2
    Loose coupling не предназначен для того, чтобы быстро заменять модули в системе(хоть и позволяет это). Главный его бенефит — это возможность легко тестировать и, главное, изменять существующий код. Я хочу вносить изменение в метод и быть уверенным что это изменение повлияет только на то место, где я и ожидаю, а не сломает что-то совсем левое в другой части системы.
  • +2
    По моему мнению, обилие интерфейсов со всего лишь одной реализацией немного затрудняет поддержку кода. В одном из проектов, в котором я участвую, практически все сервисные интерфейсы имеют только одну реализацию. В итоге чтобы добавить метод, нужно сначала добавить его в интерфейс, потом в реализацию. Хотя всем уже итак понятно, что в обозримом будущем других реализаций для этого интерфейса не предвидится. Но «магический фреймворк» требует интерфейсов. Т.е. по сути здравого смысла понятно, что мне тут нужен один файл, но поддерживать приходится два. А вообще ад — это когда один такой сервис через цепочку таких же сервисов взаимодействует с другим сервисом. Приходится протаскивать нужный метод путем редактирования интерфейса и реализации для каждого элемента цепи. Отсутствие здравости в том, что практически всегда это редактирование сводится к одной строчке вызова метода из другого сервиса.
    • 0
      по-разному бывает. бывает, что разработчики в состоянии аффекта действительно забыли про какую-то важную штуку, например, про третье колесо для детского велосипеда — тогда да, надо половину рамы перекраивать. а иногда пытаются фонарик к рулю приделать методом всепроникающей диффузии, вместо того, чтобы заимплементить сей девайс с интерфейсом стандартного хомута. еще говорят, что сервисы это не ооп, поэтому понятие интерфейса к ним применимо лишь чисто условно.
      • 0
        Ну, так-то да я бы сказал, что объектно-ориентированная парадигма как бы перпендикулярна сервисно-ориентированной. Но, например, параллельна классической структурной парадигме (в стиле языка C). Т.е. мы можем следовать сервисно-ориентированной парадигме как на базе ООП, так и на базе структурной парадигмы.
    • 0
      В том, что у вас все модели исходят из интерфейсов — это не есть плохо.

      Во-первых, очень удобно документировать: все что торчит наружу — торчит из интерфейса. И тестировать API тоже удобно.

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

      В -третьих, совершенно не факт, что у вас не появится в будущем другой реализации. Однако наличие абстракций в разумных пределах не сильно повышает сложность кода. Это цена, которую необходимо и можно заплатить за гибкость.
  • +1
    не понимаю, что все к этому «loose coupling» так мм… прицепились ;). вот если бы автор еще и «high cohesion» так же красочно расписал, то половины интриги могло не получиться — как раз этот принцип не дает совершать всякие глупости. как то крошить код без надобности, или пилить рамы лисапедов на кусочки длиной 1см. в теории конечно: принципы-принципами, — они не мешают делать на практике весьма причудливые вещи.
  • +3
    Заметил такое явление, значительное число Java-программистов, после того как переучиваются на другой язык, пишут код так, что видно, что писал бывший Java-программист. И даже при наличии IDE с первосортной навигацией в логике такого кода разобраться тяжело — вроде и аккуратно написано, по всем канонам, но логика размазана по файлам и помножена на слабую связность компонентов до такой степени, что разобраться в логике кода очень тяжело.

    Вроде и придраться не к чему(в рамках существующего на сегодняшний день повального увлечения совершенным кодом), а работать с таким кодом невозможно.
    • 0
      Да, сперва не поймешь, где выполнение вообще начинается. А потом с грустью в глазах ищешь в траве интерфейсов где хоть какой-то реальный код и что вообще он вызывает…
  • +2
    Хорощий код — это тот код, который решает задачи проекта при наименьшей стоимости реализации и поддержки.
    • 0
      Простите, опечатка. Хороший.
  • +1
    Согласен с положениями в статье, но не согласен с примером. Пример специально подведен к космическому построению архитектуры.
    Например: требуется велосипед. К велосипеду есть определенный набор требований, и мы их реализуем. Именно по факту требований. Нужно что-то больше? Появляются новые требования. Из-за них могут появиться первые или новые уровни абстракции, опять же в угоду требованиям, но мы не пишем всё заново.

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

    Ещё один момент: фреймворки. Они для того и нужны, что позволяют построить каркас. Но они ни разу не завязаны на требованиях к конкретной области, поэтому и стараются максимально декомпозироваться на модули. А вы уже выбираете: вам один позвонок или всё-таки позвоночник.

    А что касается дженериков в их привычном понимании — это сугубо техническая фича, направленная на решение именно технических задач, но никак не бизнесовых. В бизнесовых собственно можно наплодить абстракций уйму.
    • +1
      В статье под дженериками (generic) подразумеваются не те дженерики, которые параметризованые типы, а обобщенные абстракции.
      В остальном, как выше уже писали необходим баланс, эта статья, на мой взгляд, как раз об отсутствии баланса — о забивании гвоздей микроскопом.
      • 0
        согласен, что в примере кинулись в крайность, отсюда получилась аж целая развёртка размышлений на эту тему
  • +1
    А по-моему, хороший код — это тот код, который легко может поддерживать сторонний разработчик.

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