Устранение magic-strings в javascript

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

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

<span animate_period="10" animate_amplitude="20"></span>

И есть javascript-код, который ищет блоки с этими атрибутами и в соответствии со значениями применяет определенную анимацию. В итоге имена атрибутов (magic-strings по сути) дублируются во вьюшках/контроллерах и js-файлах.
Еще одной похожей проблемой становится посылка аякс-запросов к контроллерам:

$('#mydiv').load("http://mysite.ru/Items/GetItemInfo?id=20");

URL запроса явно грозит нам опечатками и/или ошибками, когда этот адрес изменится.
Если мы пишем яваскрипт-код прямо в html файлах (а не в отдельных подключаемых скрипт-файлах), то можно, конечно, воспользоваться T4MVC и написать что-то вроде:

$('#mydiv').load("@Url.Action(MVC.Items.GetItemInfo())");

Как раз о таком способе решения проблемы я недавно и писал. Но если js вы всё-таки выносите в отдельные файлы (а делать это надо — для уменьшения дублирования и клиентского кэширования), то проблема так просто не решается.

Столкнувшись с проблемой на довольно-таки большом проекте, в голову пришла мысль воспользоваться всей мощью шаблонов T4 и сгенерировать соответствующие javascript-хэлперы. Всё сложилось удачно (шаблон можно скачать), в результате обработки данного шаблона получается яваскрипт-файл T4MVC-JS.js, который можно легко инклюдить через тег <script> в хтмле, и так как он генерится не в рантайме и для Visual Studio ничем не отличается от обычного яваскрипт-файла, то по объявленным в нем переменным работает интеллисенс, что сокращает вероятность ошибок.
Continue reading

Светлая сторона property-injection

О плюсах инверсии зависимостей и той гибкости, которую она даёт приложениям сказано очень многое, и я искренне надеюсь, что она повсеместно используется :)
Как известно, существует несколько способов инверсии зависимостей:

  • Constructor-based injection — инъекция через конструктор. Класс объявляет свои зависимости как параметры в конструкторе:
        public class MyImageProcessor
        {
            private readonly IScreenshooter _screenshooter;
            private readonly IImageResizer _imageResizer;
            private readonly IImageComparer _imageComparer;
    
            public MyImageProcessor(IScreenshooter screenshooter, IImageResizer imageResizer, IImageComparer imageComparer)
            {
                _screenshooter = screenshooter;
                _imageResizer = imageResizer;
                _imageComparer = imageComparer;
            }
        }
    

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

  • Property-based injection — инъекция через публичные свойства. Свойства в этом случае обычно необходимо украшать атрибутами:
        public class MyImageProcessor2
        {
            [Inject]
            public IScreenshooter Screenshooter { get; set; }
            [Inject]
            public IImageResizer ImageResizer { get; set; }
            [Inject]
            public IImageComparer ImageComparer { get; set; }
    
            public MyImageProcessor2()
            {
            }
        }

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

Есть еще field-injection, когда инъекция идет не через публичные свойства, а через публичные поля, но этот вариант уже почти официально считается «говнокодом» :)

До недавнего времени в подавляющем большинстве случаев я использовал инъекцию через конструктор, и с трудом представлял причины, по которым можно предпочесть property-injection.
Continue reading