О повторном использовании кода

    Сегодня существуют разные мнения по поводу успешности объектной технологии. С одной стороны, большинство современных mainstream языков программирования являются объектно-ориентированными, с другой стороны, нередко можно услышать критику ООП, дескать, объектно-ориентированное программирование «провалилось» и не оправдало тех надежд, которые были возложены на нее индустрией разработки ПО. Все, мол, ожидали наступления вселенского счастья в виде увеличения повторного использования, упрощения сопровождения, да и вообще, обещали, что думать придется кому-то другому, а я за это буду деньги получать.


    Причин у подобных разочарований тоже несколько. Во-первых, такое отношение к ООП может быть следствием завышенных ожиданий, а ведь Фред Брукс еще двадцать лет назад писал о том, что не стоит ждать «серебряных пуль», способных на порядок увеличить продуктивность труда программиста. Во-вторых, никто из серьезных сторонников ООП (типа Гради Буча или Бертрана Мейера) не обещали, что все будет просто. ООП – это не волшебная палочка, которая сделает из любого УГ конфетку, даже обертку которой можно будет использовать повторно.

    Так вот вопрос: как же можно достичь той заветной мечты, когда системы можно будет строить из готовых компонентов, не написав при этом ни строчки кода? Я не уверен, что в таком виде эта мечта вообще осуществима из-за неотъемлемой сложности ПО, а также потому, что часто само решение влияет на решаемую задачу. Тем не менее, если направить энергию, затрачиваемую на повторное использование в правильное русло, то с разумным количеством трудозатрат можно поднять «реюз» кода на достойный уровень.

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

    Стандартный жизненный цикл ПО


    Для начала, давайте рассмотрим типичный жизненный цикл одной итерации разработки ПО:



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

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

    Преждевременное обобщение


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

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

    Проблема в том, что затраты на дизайн, реализацию и сопровождения публично доступного кода на несколько порядков выше стоимости сопровождения простого кастомного решения. Начинается все с того, что реюзабельный код требует более полной и подробной документации, лучшего качества и покрытия тестами, примеров использования и пользовательской документации. Даже если код предназначен для повторного использования внутри команды, то его качество и простота использования должны быть такими, чтобы программисту было экономически выгодно разобраться в вашем решении, а не городить свой собственный любимый огород. И я уже не говорю о необходимости культуры повторного использования, без которой вся идея «реюза» накроется медным тазом благодаря всеми любимому симптому NIH (Not Invented Here).

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

    Все эти сложности не говорят о том, что заниматься повторным использованием глупо. Нет. Аналогично тому, как оптимизация производительности должна осуществляться после профилирования, так и обобщение должно осуществляться вОвремя: не во время разработки самой «фичи», а на более поздних этапах, когда команда понимает, куда будут направлены возможные изменения и, что нужно обобщать, а что – нет.

    Модифицированный жизненный цикл итерации




    Здесь нужно понять очень важный момент: шаг обобщения не является сам по себе достаточным условием для создания реюзабельного кода, точно также как процесс профилирования не гарантирует того, что вы сможете повысить производительность найденного bottle neck-а. Это лишь этап, во время которого простое и адекватное решение, полученное на этапе проектирования и реализации «причесывается» дополнительно для доведения его до уровня повторного использования: убираются лишние зависимости, улучшается документация, добавляются дополнительные юнит-тесты. Если во время реализации все скомкать в одну кучу и сделать из системы такой клубок, что без бутыля не разобраться, то не о каком будущем «реюзе» речи быть не может. Но если начать с простой и понятной архитектуры, которая хорошо решает поставленную задачу, то объединить несколько однотипных решений в одно будет значительно легче, когда будет уже точно понятно, что у них общего.

    ПРИМЕЧАНИЕ
    В данном случае я не рассматриваю разработку библиотек, как отдельного продукта. Библиотека изначально заточена на повторное использование и там многое делается по-другому; достаточно полистать Framework Design Guidelines, чтобы понять – при разработке библиотеки на первый план выходит ее качество, удобство использования, отсутствие ломающих изменений, интуитивная понятность, «иерархичность» и много другое. Здесь же речь идет не о библиотеках для широкой общественности, а о проблемах с «реюзом» в типичных enterprise приложениях.

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

    Заключение


    К сожалению, даже этап обобщения не является залогом повышения «реюза» кода. Первая проблема связана с выбиванием у руководства дополнительного времени на непонятное обобщение, вместо того, чтобы клепать новую функциональность следующей итерации. Да, все твердят о важности повторного использования, но когда заходит речь о дополнительных расходах, то близорукость руководителей проекта может сыграть свою роль и в обобщении будет отказано. В этом есть и доля истины; вторая проблема связана с тем, что наличие качественного кода, доступного для повторного использования, не гарантирует его использования. Для нормального «реюза» нужна еще и культура, а всеми любимый NIH и жажда велосипедостроения могут быть настолько сильными, что любые попытки повторного использования ни к чему не приведут.

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

    ПРИМЕЧАНИЕ
    Идею с этапом обобщения предложил Бертран Мейер в свое книге «Объектно-ориентированное конструирование программных систем», так что немного подробностей можно найти в разделе «28.5 Обобщение» (кстати, это и правда отличная книга, в которой можно найти много интересных идей об ООП и разработке ПО).

    Комментарии 45

    • НЛО прилетело и опубликовало эту надпись здесь
        +1
        Самое печальное — это когда заказчик против использования third-party либ, а PM\TeamLead'у нафиг не надо его переубеждать, что мол 21й век на дворе, LGPL давно есть и все хорошо, прекрасная маркиза
      • НЛО прилетело и опубликовало эту надпись здесь
          +2
          Я ничего такого не предполагаю. Я говорю лишь о том, что при прочих равных условиях я выберу простое решение, которое будет понятно всем и каждому, вместо навороченного решения, которое даже мне будет непонятным через пару дней.

          >>… ключевым элементом успеха в enterpirse, является глубокие знания в прикладной области…

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

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


              В Вашем случае как и в моем, к сожалению — никогда. Я сам работаю с легаси системой одного топ 10 международного банка. Когда 99% времени уходит на поиск области изменения, анализ прикруток и 1% на вставку новой строчки кода и сборку.


              Как то сильно пессимистично. У меня тоже клиенты банки :) Без ООП и повторного использования — это конечно труба. Без деталей тут говорить конечно демагогия. Может мы попробуем сформулировать основные проблемы?

              Тут первый вопрос в том тратите ли вы время на рефакторинг ПЕРЕД внесением новой фичи? Основная проблема на мой взгляд, не в предметной области (её можно сильно и не знать, правда при условии, что есть хороший постановщик задачи/выполняющий анализ предметной области). Нужно как бы расставить в нужных местах «точки роста», позволяющие достаточно легко менять поведение.

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

            0
            Вместо скучного академического термина «преждевременное обобщение» есть гораздо более теплый и ламповый — "тьюринговская трясина" :)
              0
              Это, все же, не совсем одно и тоже. Я бы сказал, что трясина — это частный случай обобщения в данной конкретной области (в области языков программирования). Само же преждевременное обобщение (сори, сей академичный термин придумал не я) является более общим понятием, и, в то же время, более близким к программированию, а точнее, к проектированию ПО.

              З.Ы. Скучное понятие паттерна нельзя заменить другим более теплым понятием «идиома», даже если в каком-то контексте эти понятия могут пересекаться. У каждого понятия свой смысл и свое место, так что не вижу причин, почему бы не использовать каждое понятие по назначению.
                0
                Эта идиома сейчас применяется более широко, в том числе и для названия фреймворков, делающих все одновременно. Подумалось, что упомянуть ее в комментах стоит.
              +3
              Уже десять лет только на моей памяти появляются статьи, смысл которых сводится к тому, что ООП — это не универсальный вычислитель комплексных переменных, а обычный молоток.

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

              А отверстия в стене, сделанные молотком, никуда не годятся.

              Да и доски пилить молотком хреново выходит.
                +2
                И десять лет приводят аналогии с молотками, шурупами и тп. :)
                  0
                  И спустя десять лет ничего не изменится.
                +1
                Вот работаю сейчас в таком проекте без преждевременного обобщения.

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

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

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

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

                        Во-первых, рекомендую полистать Framework Design Guidelines, чтобы появилось ощущение того, что принципы, лежащие в основе фреймворков отличаются от стандартных принципов проектирования. Поскольку основная цель фреймворков — это их повторное использование, то там подходят очень по другому к публичному (точнее, в этом случае к «публикуемому» интерфейсу), юзабилити, производительности и т.д. Зачастую эти принципы настолько важны, что разработчики фреймворков могут жертвовать такими общепринятыми принципами, как SRP, только в угоду юзабилити или производительности.

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

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


                          Видимо именно так. Давайте попробуем разобраться. Слово «фреймворк» зачастую сильно вводит в заблуждение, наиболее точным русскоязычным аналогом будет «архитектурный каркас». Т.е. есть два вида повторного использования: (1) низкоуровневые функции, типа работы с файлами, базой данных, и т.д. (2) управляющий код, который управляет высокоуровневыми процессами обработки. В первом случае говорят о ядре, реализованном скажем в отдельной .dll библиотеки. Во втором говорят о каркасе — достаточно зарегулированном способе написания кода для него. Если в первом случае мы вызываем функции из .dll, то во втором каркас вызывает функции написанные нами.

                          Вот и вся разница. Концепция того в какой мере используется ядро+каркас+предметная область+контроль средств разработки = архитектура.

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

                          Теперь я хотел бы, что вы достаточно четко определили как вы это себе представляете.
                            0
                            Ок. Давайте проясним еще несколько моментов. Первое: определение термина фреймворк.
                            Framework is… " It is a collection of software libraries providing a defined application programming interface (API).
                            A software framework is a universal, reusable software platform used to develop applications, products and solutions."

                            Так что здесь разница не только в push и в pull-модели общения. Но даже если не в даваться в дебри, суть в том, что фреймворк по своему определению — это «reusable software platform», как ни крути, а это, в свою очередь значит, что: (1) его сложно разрабатывать, (2) его дорого поддерживать, (3) его нельзя разработать при разработке одного приложения, поскольку это будет противоречить пункту «reusable sotware platform».

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


                              Тут я с вами согласен, ниже я писал

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

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


                              «Ваше» определение фреймворка мне кажется не очень удачным, API в фреймфорке дело десятое. И еще есть ньюанс: "(3) его нельзя разработать при разработке одного приложения, поскольку это будет противоречить пункту «reusable sotware platform»."

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

                              Это общая вещь для множества форм-документов. Т.е. в приложении у нас таких может быть 10-20 шт. и начать писать для этого часть фреймфорка мы уже сможем после третьей-четвертой — ведь опыт у нас уже есть !? И начнется этот фреймфрк с того, что мы захотим контролировать показ форм, и как правило для этого создадим фреморковский класс Менеджер-форм. Этим измениться дисциплина вызова всех форм, теперь программист будет пользоваться этим классом, а не прямо вызывать форму.

                              Со временем (где-то в средине разработки) окажется, что например в C#, мы захотим пользоваться не только WF, но и WPF. А WPF окажется, что не поддерживает многооконную дисциплину. Поэтому менеджеру форм придется часть реализации совмещения WF и WPF скрыть от пользователя-программиста.

                              А теперь подумаем, что означает не начинать создавать фреймфорк, в данном случае менеджер форм. Каждый программист будет реализовывать функции показать, редактировать, новый по своему, причем если он не сильно дисциплинирован, то даже у одного будет отличаться то как это он реализует для разных форм. И я уже не говорю о том, сто вариантов совмещения WF и WPF множество. Но ведь программисту не важно, чтобы это работало всегда и насколько это будет эффективно — он не будет тратить время, он выберет частный случай, которые ему подходит, а скорее всего первый попавшийся (ведь начальник ему сказал реализуй форму-документ — это его цель, поэтому на побочные он потратит на порядок меньше времени, т.к. платят ему за другое). В итоге проект уже обрастет бородой, и преобразовать его в стройную идею будет куда сложнее, чем если бы не делать зачатки фреймфорка.
                      0
                      Увы, без покрытия тестами увязнуть в «простом» коде можно очень быстро. Я бы к этому и перешёл в вашем проекте, пока клубок не завязался ещё сильнее.
                        0
                        Не знаю, может я чего-то не понимаю, но на практике покрытие тестами сделать практически не реально. Тут получается так, что даже наоборот их наличие тянет тебя к земле — надо постоянно поддерживать тесты, вместо того, чтобы рефакторить код. И как правило поддержка актуальности тестов съедает на порядок большее время, чем более аккуратный постепенный рефакторинг с последующим тестированием «черного ящика». Да, периодически тестировщикам приходится ловит якобы одни и те же ошибки, но они как правило выявляют различные варианты использования GUI, которые так или иначе тестами не покроешь. И тогда спрашивается — а ради чего?
                          0
                          Если тестируется абстракция (т.е. то, что должен делать класс, его контракт), а не то имплементация (то, как он реализован), то тесты на пустом месте не ломаются. Они в таком случае ломаются только тогда, когда нарушена логика, которую они призваны защищать.

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

                          З.Ы. Если у вас в проектах не удавалось добиться адекватного покрытия (я, кстати не говорю за 100%), то это не значит, что это невозможно, это говорит лишь о том, что вы не умеете это дело готовить.
                            0
                            Ок, мы по крайней мере поняли какими признаками обладают плохие тесты, и согласились, что их нет смысла делать. Теперь осталось понять, а существуют и могут ли существовать хорошие тесты.

                            Вы считаете, что надо покрыть тестами каждый класс, и при этом его public часть. Действительно. якобы наличие у класса четкой задачи должно приводить к замкнутому конктракту, который класс должен выполнять. Но давайте подумаем. Вот вы говорите тут, что мы не имеем возможности во время разработки, что-то обобщать и выделять повторно используемые части. Так?

                            С другой стороны, что мы делаем когда наделяем классы задачами/ответственностью за что-либо? В слое ответственном за предметную часть, будут такие классы как Человек, Организация, Сделка, и т.д. Но будут ли они полны? Думаю нет, они всегда будут специфичны для той задачи которую мы решаем, т.е. в зависимости от задачи мы сделаем ту или иную абстракцию. И будем её совершенствовать только по мере нашего понимания и углубления в задачу.

                            Т.е. тесты мы сможем писать тоже постепенно, ведь мы не знаем вначале какую полноценную абстракцию должен реализовать тот или иной класс. Мы двигаемся постепенно, аналогично тому как не можем (согласно вам) на этапе проектирования обобщать. И делаем это лишь после того как точно узнали какую абстракцию должен поддерживать класс. Но когда мы это знаем — зачем на к тому времени какие-то тесты? Зачем нам тогда что-то обобщать? Совершенно не зачем — мы так уже все написали и отладили.

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

                            А теперь взгляните на то, что такое хороший тест? Не является ли хорошие тест — зачатком обобщения для повторного использования? Не выполняет ли повторное использование роль тестов?
                              0
                              Я считаю, что нужно покрывать тестами разумную часть абстракции. Это позволит понять как ее целостность (на данный момент), так и ее качество.

                              Не нужно путать публичный интерфейс (public часть) и опубликованный интерфейс (используемый кем-то еще, published часть). Качественный публичный интерфейс (и, ессно, его реализация) является необходимым условием простоты его последующей публикации.

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

                              А теперь взгляните на то, что такое хороший тест? Не является ли хорошие тест — зачатком обобщения для повторного использования? Не выполняет ли повторное использование роль тестов?


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

                              З.Ы. Фаулер десять лет назад писал о разнице между публичным и опубликованным: PublishedInterface

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

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

                                Так что тогда тестировать в таком случае?
                            0
                            Если писать тесты до написания кода, то всё станет через время на свои места. Они, да, сдерживают полет фантазии и обрезают крылья.
                            Но так и надо. Через время, такое сдерживание заставляет и к коду относиться иначе. Стараться писать его более кратко, более четко выражая мысль. А тесты по сути становятся для разработчика не тестами — а требованиями. Он уже интуитивно считает написание тестов — это написание требований, которым должен удовлетворять код. Тесты делают краткими и простыми. А когда проводят рефакторинг, как раз в этом и сила тестов — они падают. И их надо поправить.

                            Если бы их не было, то как-то стремно код менять. Что-то падает только в платоновском мире идей. А когда оно здесь упадет — неизвестно.

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

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

                            Потом, я переписал и перелопатил и изменил логику всего движок и еще одну важную систему в текущей конторе. Эта система работала уже. Любые сбои дорого стоят. И приятно, когда я два раза выкатывал практически на ходу — и всё бесшовно.

                            И на тесты не так много времени ушло. И на поддержку на самом деле не так много уходит.
                          0
                          «так как нет тестов»

                          Эта фраза всё убила. Нет тестов — следовательно нет рефакторинга. Кстати, и не будет нормального рефакторинга, если нет тестов.

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



                                Все эти сложности не говорят о том, что заниматься повторным использованием глупо. Нет. Аналогично тому, как оптимизация производительности должна осуществляться после профилирования, так и обобщение должно осуществляться вОвремя: не во время разработки самой «фичи», а на более поздних этапах, когда команда понимает, куда будут направлены возможные изменения и, что нужно обобщать, а что – нет.


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

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

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

                                        К сожалению, именно так — именно не все (а не «не всё»), этим и отличается программист от архитектора.
                                          0
                                          Я так понимаю, что архитектор (сори, но у нас должности аналогичные;) ) всегда может всЁ это (и обобщение тоже) сделать на этапе проектирования?

                                          Это сделать сложно не только в силу слабости архитектора, но и потому, что очень часто решение меняет задачу. Как быть в этом случае?
                                            0
                                            «решение меняет задачу» — я не очень точно понимаю какой смысл вы вкладываете в эти слова, может пояснить подробнее, а лучше с примером.
                                      +1
                                      Вставить обобщение между проектированием и разработкой сложно по нескольким причинам. Во-первых, эти этапы слишком сильно связаны. Хоть сейчас даже agile адепты признают важность архитектуры, но слишком уж формального этапа проектирования никто все равно не делает. Обычно на этом этапе рисуются диаграммы классов, расписываются типовые юз-кейсы, указываются контракты.

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

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

                                      Кроме того, бывает что решение влияет на исходную задачу. Это тоже бывает, ведь в процессе решения заказчик и команда могут лучше понять, что же на самом деле нужно. Тогда попытка обобщить до этого момента просто провалится по определению.
                                        0
                                        Все это верно, когда вы говорите о слое предметной области. Ну и только. Причем такой предметной области с которой вы столкнулись впервые. Но кроме этого слоя есть ряд других технических слоев. Абстракция работы с базами данных, событиями, формами, стилистика ведения диалогов с пользователем, решение как реализовать идею Model-View-Controler и реализовывать ли её вообще. Все это и есть основы архитектуры. Если программисту сказать вот тебе язык программирования такой-то и делай как считаешь нужным. Все ставим точку — у вас в проекте просто нет и не будет архитектуры.

                                        Конечно, архитектура не появляется у программиста с его первым проектом. Ну, и вообще-то не очень серьезно доверять человеку после института должность архитектора ПО. Архитектором ПО как правило становится не тот кто умеет рисовать красивые диаграммки на UML, а такой программист, который разработал ряд общих техник как программировать ту или иную типовую задачу (обозначим F) с переменной предметной областью (обозначим x). Т.е. у него есть опыт построения функций F(x).

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

                                        Но если в компании налажен процесс разработки, то он выглядит несколько иначе. Архитектор задает и определяет F, а остальные программисты кодируют x, y, z… И тогда задача архитектора состоит в том, чтобы его F соответствовала потребностям x, y, z, но так чтобы по возможности программист выполнял ряд ограничений, не нарушающих согласованность между x, y, z. Именно эту роль должна играть архитектура, и она не может быть создана постафактум. Она или есть вначале или её просто пока нет. Ожидать что принципы архитектуры оценят и поймут все программисты — это не серьезно, им надо их просто выполнять… чего порой не любят программисты.
                                      0
                                      т.е. во время разработки надо возвратить код в русло архитектуры, разработанной во время проектирования архитектором. Но если с архитектурой, что-то не так — то не пытайтесь переделать работы архитектора — путь пашет сам, и указывает вам как сделать то или иное, т.е. только он имеет право исправлять дефекты архитектуры во время разработки. Именно это и будет его опытом для следующего проекта.
                                        0
                                        Так вот вопрос: как же можно достичь той заветной мечты, когда системы можно будет строить из готовых компонентов, не написав при этом ни строчки кода?
                                        Уже можно
                                          0
                                          Мне кажется место этапа «Обобщение» не в конце текущей итерации, а после «Анализа требований» следующие итерации (или другого проекта). Похоже на рефакторинг для облегчения внесения изменений, только в общем случае охватывающий несколько проектов.

                                          Не до конца понятно только, как это рисовать.
                                            0
                                            Читал когда-то здесь sergeyteplyakov.blogspot.com/2012/04/blog-post_19.html, одобряю.

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

                                            Самое читаемое