В этой теме я хотел бы поразмышлять над вопросом, о котором несколько раз уже задумывался... Прошу извинения за то, что размышления будут вольно-тезисно-образными
"Оформлять" нет никакого желания(=надобности), а просто позабыть за другими делами жалко
Так вот, представим себе, что мы разрабатываем некоторую новую подсистему X. И для её функционирования нужны некоторые отдельные средства уже существующей (пусть для контрастности - не нами производимой) подсистемы Y. Мы являемся знатоками и ценителями компонентного программирования, одно из основных качеств которого, как известно, эффективное повторное использование и интеграция подсистем, созданных разными разработчиками. И, казалось бы, совершенно прозрачная ситуация. Мы в очередной раз скажем спасибо компонентности, разработчикам Y, используем пару-тройку замечательных средств оттуда (на самостоятельную разработку которых ушёл бы не один день) - сделаем нашу долгожданную систему X. Идиллия.
Однако время идёт, "экосистема", в рамках которой мы работаем, обрастает "мясом", и мало-помалу обнаруживается, что множество отличных самих по себе компонент, оказывается сильно пронизанными взаимными связями. Вот уже и пользователь ворчит, когда для установки, как ему справедливо кажется, весьма простой и маленькой компоненты, ему приходится закачивать и развёртывать ещё два-три гораздо более весомых компонента (и ему ну никак не понять, каким образом набор элементов управления зависит от библиотеки, предназначенной для синтаксического разбора). А разработчики испытывают всё больше проблем с взаимной совместимостью и проч. А если разработка серьёзная, с "перспективами", то встаёт ещё и неизбежный вопрос "на что делать ставку". Какова сопровождаемость, открытость, лицензионная политика того компонента, который можно было бы, не задумываясь, применить сегодня, если бы точно знать, что будет завтра... Если компонентов-аналогов несколько, то встаёт трудный выбор между ними, а если таковой только один - то ещё более трудный выбор: опереться на него, или разработать самим.
В общем, я тут живописал что-то похожее на проблему
Что это? Обратная сторона КОП, которая есть у любой технологии?
А ничего не напоминает? Подзабытые проблемы с тем, заводить ли локальные переменные и параметры, если одна большая и глобальная где-то уже есть... Продублировать ли две-три строчки кода в разных местах сложного алгоритма или "зацепить" их хитрой GOTO-траекторией... С одной стороны, структурное и модульное программирование стало основой повторного использования "в большом", но в малом-то оно как раз убрало повторное использование в плохом его виде! Была "доструктурная анархия" - и не надо было думать, "чьё это и где это" - если есть, то цепляй! используй! напрямую... в любом виде... Пришла модульность - и уже возникают понятия "своё-чужое", "внутреннее-внешнее". И если кто-то не захотел вывести из модуля A наружу ну очень вам нравящуюся процедурку, то хоть лоб расшиби - а придётся "дублировать", "строить велосипед", ну или при возможности "жульничать" копипастом.
Так вот к чему я тут весь этот сумбур изливаю... Модульность победила, благодаря ей стала возможной компонентность. Но старые проблемы-то имеют свойство вылезать в новом виде на новые уровни
Не имеем ли мы "макаронный стиль", "выдавленный" на уровень компонент? И не придёт ли пора задуматься о масштабировании принципов модульности на целые подсистемы... Вспомним тут же про принцип теории систем, про который нам говорил недавно Владимир Лось (про предельный порог внешней связности 13%) -
viewtopic.php?p=11873#p11873("Короче, Склифософский"... поближе к прахтике...) Биясь иногда об сию дилемму (использовать - не использовать, скопипастить, переписать...) я выработал для себе примерное правило действий. Сначала нужно понять "контекст", в котором подсистема будет разрабатываться, работать и развиваться далее. В этот контекст сразу попадают какие-то основные сторонние компоненты/библиотеки, опора на которые нас вполне устраивает, без которых мы разработки не мыслим, которым доверяемся на все 100... И "проблема" их использования нас никак больше не волнует, т.к. и проблемы тут никакой нет. С другой стороны, подсистемы, не попавшие в "контекст", воспринимаются как нежелательные для нас, их использование трактуется как вынужденная мера. Далее смотрим по ситуации: если речь идёт об отдельных мелких средствах (процедурках), то использованию внешнего предпочитаем своё (и тут опять же по ситуации: другая подсистема своя или открытая, совпадающая с нами по лицензии - копипастим. Закрытая - пишем сами).
Самый интересный случай - подсистема из категории "нежелательных", но требуется весьма серьёзная служба из неё (и в большинстве случаев извлечь только одну службу нельзя, она плотно связана с остальной частью подсистемы). И здесь на помощь приходят как раз-таки компонентные паттерны. Если использования внешней подсистемы избежать нельзя, то пусть про неё хотя бы знает как можно меньшая часть нашей. В идеале - какой-нибудь один специальный маленький модуль. Опишем в модуле SomeService абстрактные интерфейсы к той службе, при этом предельно компактные - включающие в себя доступ ровно к тому функционалу, какой будет нужен сейчас и в ближайшем будущем нашей подсистеме. И сделаем модуль "реализации" SomeService-Based-On-External-Component-X, который позволяет использовать внешний компонент через эти абстрактные разъёмы.
Что это даёт? Конечно, использования стороннего компонента мы не избежали. Но теперь он - не незаменимый, а его выбор далеко не такой ответственный, как бывает обычно. Он - всего лишь конкретная реализация нужной нам службы. Одна из таких реализаций. Время сэкономили, велосипедов не строили - а риск минимален. "Прижмёт" в будущем - выкинем, вставим в продуманный разъём другой. Напишем сами, когда будет на то возможность... И т.п. В общем, разрешили нарисовавшуюся проблему компонентности обычными её средствами. Правда, мы больше привыкли к системному назначению этих средств ("разъёмы", "подключаемые реализации")... Для чего абстрагироваться от системы, нам давно ясно. А вот оказывается, что полезно не только от системы, но и от такого же "соседа - честного компонента".
Такая вот история у меня вышла. А ещё попробовал я из неё мораль, как полагается, извлечь
И нарисовался некий принцип, который условно назову принципом "пространственно-временной локализации".
Пространственная локализация заключается в том, что система устроена тем лучше, чем меньше каждая её часть знает о других её частях.
Нуу, это очевидно... Ведь по этому пути и развивались высокоуровневые методы программирования - структурное, модульное программирование, технология ООП... Однако и по сей день мы часто нарушаем этот принцип. А нужно наоборот - начинать применять его к новым реалиями. Например, к компонентности.
Временная локализация менее очевидна. Система получается тем лучше, чем на меньшее число предположений о будущих задачах/развитии она опирается. Подчеркну - не разработчик более "узколобый", а меньше конкретики относительно будущего заложил в свою систему в настоящем. Разработчик готов к тому, чтобы решать новые задачи завтра, тогда, когда они реально появятся и станут ясны. А его система готова к лёгкой адаптации и расширению под эти конкретные задачи.
И наконец, стоит обратить внимание на те механизмы, которые лежат в основе этой пространственно-временной локализации (ПВЛ). В частности, это механизмы позднего связывания/виртуализации (ОО-средства, шина сообщений Оберона и т.п.). Их "соль" как раз в том, что взаимодействующие части могут знать друг о друге на порядок меньше, чем при традиционном программировании.
А, осмеливаясь на некоторое обобщение, выскажу гипотезу, что основой для реализации ПВЛ являются обратные связи в системе. Действительно, если мы попытаемся, следуя ПВЛ, разбить на обособленные части некоторую систему, и попытаемся это сделать в рамках однонаправленных иерархических связей (обычный статический импорт), то во многих случаях нам это не удастся. Слишком сильна связность внутри системы. Можно опустить руки и оставить всё как есть. А можно использовать динамические связи и компонентные паттерны - и успешно решить проблему.
А для реализации временной локализации важны обратные связи в процессе разработки системы. Если мы попытаемся работать линейно (водопадная модель), то сегодня мы должны угадать как можно больше из того, что будет завтра, потому что завтра уже не сможем поправить то, что сделали сегодня. Если же будем работать итеративно, возвращаясь, пошагово уточняя, то всё получится.
И в заключение выдвину ещё одно предположение. Мы знаем наш метапринцип - ПК ("принцип Калашникова"). Возникает естественный вопрос: где остановится, где предел простоты? Одно из ограничений известно - простота отличается от примитивизма тем, что даёт принципиально эквивалентный функционал (урезается не задача, а её решение и часто побочные её "наросты", вызванные неверной постановкой и т.п.)
Я бы поставил ещё одно ограничение (конечно, оно и так уже интуитивно многими "калашниковцами" ощущается) - упрощаем до тех пор, пока это не входит в серьёзное противоречие с ПВЛ. До этого порога мы экономим за счёт "излишков", а далее экономия сегодня выйдет нам боком завтра....