Помнится, во времена .NET 1.1 и 2.0 можно было часто увидеть пророчества майкрософтовских евангелистов, мол, скоро любая домохозяйка сможет создавать сайты и писать программы. Большинство программистов посмеялось, но, как выяснилось, кто-то отнёсся к этому серьёзно. Во всяком случае, это объясняет, почему шаблоны проектирования IoC/DI получили второе дыхание в 2000-х, причём даже внутри самой MS (желаю Вам никогда в жизни не столкнуться с SCSF).

С точки зрения теории разработки ПО лично мне гораздо чаще приходилось читать или слышать хвалебные статьи и отзывы об IoC/DI, но, как всегда, критика тоже есть. Можно ознакомиться, например, здесь (англ.), здесь (англ.), тут (Хабр), ещё (англ.). В частности в вину ставится нарушение принципа инкапсуляции в ООП.
Но мне хотелось бы не вдаваться в теологические диспуты (в чём я не считаю себя экспертом), а остановится на трудовых буднях (которые мало, на мой взгляд, освещаются в публикациях).
Действительно, много ли Вы встречали книг или статей по программированию, где указывалось бы на то, что код всегда содержит ошибки (даже калькулятор невозможно покрыть 100% тестированием) и нужно в него вставлять возможности диагностирования ошибок в продуктивной среде, где Вам никто не даст поставить Studio и отладиться? Что если продукт окажется достойным и найдёт своего пользователя, то он обязательно будет дорабатываться, но делать это, вполне вероятно, будут другие люди, с не известно каким уровнем подготовки? Вот я ни одной не могу вспомнить.
Волею судеб В следствие личных и чужих управленческих ошибок, мне довелось запускать проект в гордом одиночестве и погрузиться в код, до чего у меня ранее не было особого желания. Использование в одном из модулей IoC/DI не было самой большой проблемой, но зато самой яркой и запоминающейся, тем более, что мне уже приходилось сталкиваться с этим чудом абстракционистской мысли на предыдущем месте работы.
Итак.
Где-то читал в своё время – то ли у Брукса, то ли у Листера с ДеМарко, точно не помню, – что языки программирования придуманы не для машин, а для людей. Машине, в конце концов, без разницы, забьёте ли вы руками в файл нолики и единички или сначала напишите текстовые команды, а затем откомпилируете в исполняемый код. Компилятору всё равно, будет ли программист вставлять комментарии в свой код или посчитает, что тот является самодокументированным. Интерпретатор JavaScript одинаково обработает обфусцированный, сжатый скрипт и отформатированный, с человекопонятными названиями переменных и функций.
Про архитектуру и парадигмы программирования можно сказать то же самое: всё это придумано для человека, а не для машины, чтобы облегчить ему задачу написания программ, повысить уровень их сложности. Поэтому первым и самым главным минусом, по моему глубокому убеждению, шаблонов IoC/DI является распределение логики по разным кускам проекта, что сильно усложняет понимание и восприятие решения в целом. Точнее – проблема не в собственно разорванности, а в том, что очень сложно всё это связать воедино в статике, только в рантайме.
Если ваша программа состоит из десятка-другого объектов (т.е. это не более 100 файлов с описанием классов и интерфейсов), то воспринять всё это вместе в виде единого целого относительно просто. Мне в своё время довелось сопровождать настольное приложение, созданное на основе Microsoft Smart Client Software Factory (потом ему на смену MS запустила Prism, но уверен, что IoC/DI там так же плотно задействованы), по своему функционалу не такое уж сложное, но состоявшее из пары десятков проектов (в терминах Visual Studio), а это сотни и сотни классов, отвечающих и за DAL, и за логику, и за пользовательский интерфейс, и за событийную модель под ним. Каждый раз, когда на горизонте появлялась задача по добавлению новой фичи, меня начинало слегка колотить изнутри, т.к. всплывала перспектива увлекательно провести несколько дней, чтобы ��огадаться, куда нужно «воткнуть» обработку нового поля объекта из БД, точнее – по каким классам распиханы зависимости. При слабой связанности классов, поверьте, это не самая тривиальная задача.
Возможно, мой мозг начал костенеть и стал менее восприимчив к новым идеям (хотя IoC/DI были придуманы, кажется, в начале 90-х), но мне сложно понять, чем стал неугоден принцип инкапсуляции из ООП.
Вспоминается цитата с башорга:
(*) фраза была несколько смягчена, дабы не навлекать на Ресурс.
Смешно, не правда ли? А вот такиешутки штуки я встречал в своём проекте на этапе запуска в ПЭ (и было мне не совсем смешно):
Очень информативно, согласитесь? BBB, DDD, NNN – это я намеренно изменил название проектов и пространства имён, которые указывали на наименование компании-субподрядчика. Но там ничего интересного для отладки не было. Dispatcher.Start() – это запуск службы MS Windows, точка входа в программу. StructureMap – это библиотека IoC. Ни единого упоминания какого-либо из бизнесовых методов, т.к. было сделано всё, чтобы исключить контекст из стека вызова.
Впрочем, причину ошибки удалось всё равно выяснить по методу «что изменилось по сравнению с прошлой рабочей версией?», но ей хочу посвятить отдельный аргумент, идущий следующим.
Как говорится, беда не приходит в одиночку. Так и IoC/DI идут рука об руку вместе с шаблоном Service Locator, который тесно связан с идеей позднего связывания. При этом при компиляции решения не проверяется, соответствует ли сигнатура методов требованиям в точке вызова.
Так и случилось в моём случае из примера выше. В один из бизнесовых методов был добавлен новый параметр. Проект успешно скомпилировался, но отказался запускаться. Мне повезло.
Во-первых, в данном проекте было всего лишь около 50 классов и методом [научного] тыка удалось относительно быстро установить, что нужно ещё доработать класс загрузки конфигурации.
Во-вторых, исключение выбрасывалось в самом начале работы программы, и мимо этого было не пройти. К сожалению, не смог найти другой реальный пример и жизни из этого же проекта, т.к. сразу не отложил «в мемориз», а потом всё затерялось в килотоннах логов. Но инстанцирование обработчиков может производиться не сразу при старте приложения, а по мере необходимости – когда появятся пользовательские данные, требующие соответствующей обработки. Это может произойти спустя часы, дни и даже недели. Однако эффективность упомянутого выше метода поиска ошибок «было – стало» обратно пропорциональна времени, прошедшему с момента проблемного релиза, и количеству релизов, последовавших за ним.
Сами по себе сложность восприятия кода и трудности отладки не смертельны. Беда в том, что для целей внедрения, сопровождения и развития продукта нужны люди из плоти и крови. И очевидно, чем сложнее продукт, тем выше требования к уровню подготовки специалистов. А тут уже встают проблемы рынка труда и сферы образования, о которых, полагаю, не нужно никому тут рассказывать: грамотных, опытных спецов найти занимает много времени, содержать не дёшево, удержать и того сложнее. Да и на изучение «матчасти» уходят недели и даже месяцы.
Как следствие из данного аргумента хотел бы выделить ещё 3 уточнения.
В начале статьи были приведены ссылки на статьи, в некоторых из коих приводятся условия, когда можно применять DI/IoC, а когда этого лучше не делать. Я предлагаю дополнить список, когда НЕ надо использовать эти шаблоны, правилами, скорее, управленческого уровня, нежели из области компьютерных наук. Не используйте таковые шаблоны, если:

С точки зрения теории разработки ПО лично мне гораздо чаще приходилось читать или слышать хвалебные статьи и отзывы об IoC/DI, но, как всегда, критика тоже есть. Можно ознакомиться, например, здесь (англ.), здесь (англ.), тут (Хабр), ещё (англ.). В частности в вину ставится нарушение принципа инкапсуляции в ООП.
Но мне хотелось бы не вдаваться в теологические диспуты (в чём я не считаю себя экспертом), а остановится на трудовых буднях (которые мало, на мой взгляд, освещаются в публикациях).
Действительно, много ли Вы встречали книг или статей по программированию, где указывалось бы на то, что код всегда содержит ошибки (даже калькулятор невозможно покрыть 100% тестированием) и нужно в него вставлять возможности диагностирования ошибок в продуктивной среде, где Вам никто не даст поставить Studio и отладиться? Что если продукт окажется достойным и найдёт своего пользователя, то он обязательно будет дорабатываться, но делать это, вполне вероятно, будут другие люди, с не известно каким уровнем подготовки? Вот я ни одной не могу вспомнить.
Итак.
Сложность для понимания
Где-то читал в своё время – то ли у Брукса, то ли у Листера с ДеМарко, точно не помню, – что языки программирования придуманы не для машин, а для людей. Машине, в конце концов, без разницы, забьёте ли вы руками в файл нолики и единички или сначала напишите текстовые команды, а затем откомпилируете в исполняемый код. Компилятору всё равно, будет ли программист вставлять комментарии в свой код или посчитает, что тот является самодокументированным. Интерпретатор JavaScript одинаково обработает обфусцированный, сжатый скрипт и отформатированный, с человекопонятными названиями переменных и функций.
Про архитектуру и парадигмы программирования можно сказать то же самое: всё это придумано для человека, а не для машины, чтобы облегчить ему задачу написания программ, повысить уровень их сложности. Поэтому первым и самым главным минусом, по моему глубокому убеждению, шаблонов IoC/DI является распределение логики по разным кускам проекта, что сильно усложняет понимание и восприятие решения в целом. Точнее – проблема не в собственно разорванности, а в том, что очень сложно всё это связать воедино в статике, только в рантайме.
Если ваша программа состоит из десятка-другого объектов (т.е. это не более 100 файлов с описанием классов и интерфейсов), то воспринять всё это вместе в виде единого целого относительно просто. Мне в своё время довелось сопровождать настольное приложение, созданное на основе Microsoft Smart Client Software Factory (потом ему на смену MS запустила Prism, но уверен, что IoC/DI там так же плотно задействованы), по своему функционалу не такое уж сложное, но состоявшее из пары десятков проектов (в терминах Visual Studio), а это сотни и сотни классов, отвечающих и за DAL, и за логику, и за пользовательский интерфейс, и за событийную модель под ним. Каждый раз, когда на горизонте появлялась задача по добавлению новой фичи, меня начинало слегка колотить изнутри, т.к. всплывала перспектива увлекательно провести несколько дней, чтобы ��огадаться, куда нужно «воткнуть» обработку нового поля объекта из БД, точнее – по каким классам распиханы зависимости. При слабой связанности классов, поверьте, это не самая тривиальная задача.
Возможно, мой мозг начал костенеть и стал менее восприимчив к новым идеям (хотя IoC/DI были придуманы, кажется, в начале 90-х), но мне сложно понять, чем стал неугоден принцип инкапсуляции из ООП.
Малоинформативные отладочные данные
Вспоминается цитата с башорга:
#define TRUE FALSE //счастливой отладки, уроды (*)(*) фраза была несколько смягчена, дабы не навлекать на Ресурс.
Смешно, не правда ли? А вот такие
Stack Trace
StructureMap.StructureMapException: StructureMap Exception Code: 202
No Default Instance defined for PluginFamily System.Func`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
at StructureMap.BuildSession.<.ctor>b__0(Type t)
at StructureMap.Util.Cache`2.get_Item(KEY key)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.Container.GetInstance[T](String instanceKey)
at NNN.BBB.Integration.Uvhd.Dispatcher.Start() in j:\.projects\DDD\trunk\NNN.BBB.UvhdIntegrationService\Dispatcher.cs:line 30
No Default Instance defined for PluginFamily System.Func`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
at StructureMap.BuildSession.<.ctor>b__0(Type t)
at StructureMap.Util.Cache`2.get_Item(KEY key)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.BuildSession.CreateInstance(Type pluginType)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
at lambda_method(Closure, IArguments )
at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.<>c__DisplayClass2.<CreateBuilder>b__0(IArguments args)
at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
at StructureMap.Container.GetInstance[T](String instanceKey)
at NNN.BBB.Integration.Uvhd.Dispatcher.Start() in j:\.projects\DDD\trunk\NNN.BBB.UvhdIntegrationService\Dispatcher.cs:line 30
Очень информативно, согласитесь? BBB, DDD, NNN – это я намеренно изменил название проектов и пространства имён, которые указывали на наименование компании-субподрядчика. Но там ничего интересного для отладки не было. Dispatcher.Start() – это запуск службы MS Windows, точка входа в программу. StructureMap – это библиотека IoC. Ни единого упоминания какого-либо из бизнесовых методов, т.к. было сделано всё, чтобы исключить контекст из стека вызова.
Впрочем, причину ошибки удалось всё равно выяснить по методу «что изменилось по сравнению с прошлой рабочей версией?», но ей хочу посвятить отдельный аргумент, идущий следующим.
Нивелирование достоинств компилируемых языков
Как говорится, беда не приходит в одиночку. Так и IoC/DI идут рука об руку вместе с шаблоном Service Locator, который тесно связан с идеей позднего связывания. При этом при компиляции решения не проверяется, соответствует ли сигнатура методов требованиям в точке вызова.
Так и случилось в моём случае из примера выше. В один из бизнесовых методов был добавлен новый параметр. Проект успешно скомпилировался, но отказался запускаться. Мне повезло.
Во-первых, в данном проекте было всего лишь около 50 классов и методом [научного] тыка удалось относительно быстро установить, что нужно ещё доработать класс загрузки конфигурации.
Во-вторых, исключение выбрасывалось в самом начале работы программы, и мимо этого было не пройти. К сожалению, не смог найти другой реальный пример и жизни из этого же проекта, т.к. сразу не отложил «в мемориз», а потом всё затерялось в килотоннах логов. Но инстанцирование обработчиков может производиться не сразу при старте приложения, а по мере необходимости – когда появятся пользовательские данные, требующие соответствующей обработки. Это может произойти спустя часы, дни и даже недели. Однако эффективность упомянутого выше метода поиска ошибок «было – стало» обратно пропорциональна времени, прошедшему с момента проблемного релиза, и количеству релизов, последовавших за ним.
Повышенные требования к уровню подготовки специалистов
Сами по себе сложность восприятия кода и трудности отладки не смертельны. Беда в том, что для целей внедрения, сопровождения и развития продукта нужны люди из плоти и крови. И очевидно, чем сложнее продукт, тем выше требования к уровню подготовки специалистов. А тут уже встают проблемы рынка труда и сферы образования, о которых, полагаю, не нужно никому тут рассказывать: грамотных, опытных спецов найти занимает много времени, содержать не дёшево, удержать и того сложнее. Да и на изучение «матчасти» уходят недели и даже месяцы.
Как следствие из данного аргумента хотел бы выделить ещё 3 уточнения.
- Я на своей практике не раз сталкивался с ситуацией, когда первоначальную команду разработчиков постепенно сменяла другая, с менее продвинутыми знаниями в теории программирования и передовых методах разработки софта. Просто потому, что после запуска продукта, бывает, на первый план выходит знание предметной области (например, законодательство, бизнес-процессы целевой аудитории, коммуникативные навыки), а не технологий кодирования. В такой ситуации чем сложнее код, тем быстрее продукт «эволюционирует» в замысловатое ожерелье костылей и заплаток.
- Чем сложнее код, тем дольше длится погружение в проект новичка.
- Не менее очевидно, что сужается круг участников команды, кто может внести свой вклад в код. Кто-то посчитает это даже плюсом. Но я сошлюсь даже не на свой опыт, а на книгу Рейнвотера «Как пасти котов. Наставление для программистов, руководящих другими программистами». Там приводится история ПМа и его программиста накануне дедлайна, когда обоих уволили из-за провала, хотя, скорее всего, они успели бы в срок, если бы руководитель помог своему программисту.
Заключение
В начале статьи были приведены ссылки на статьи, в некоторых из коих приводятся условия, когда можно применять DI/IoC, а когда этого лучше не делать. Я предлагаю дополнить список, когда НЕ надо использовать эти шаблоны, правилами, скорее, управленческого уровня, нежели из области компьютерных наук. Не используйте таковые шаблоны, если:
- Вы не уверены на 100%, что на этапе запуска проекта, его стабилизации, опытной и даже промышленной эксплуатации будут оперативно доступны разработчики этого продукта.
- Проектировщиком и разработчиком системы является один-единственный человек, который (не удивительно) не ведёт подробного документирования архитектуры своего детища.
- У вас нет основания полагать, что на этапе проектирования Вы предусмотрели все варианты использования продукта и на этапе сдаче проекта или даже после его запуска не воз��икнет острой потребности в спешном порядке «допиливать» бизнес-логику; или если бизнес-среда, в которой предстоит плавать продукту, слишком изменчива, например, частые изменения регуляторов, конкуренция на рынке, отсутствие чётких стандартов/рекомендаций/практик в отрасли.
