Рубрики
Без рубрики

Как устранить (или иметь дело с) скрытые зависимости

Shalvah Как устранить (или справиться с) скрытыми зависимостямиФотоми от Blake Cantally на программное обеспечение Unsplashin, зависимость возникает, когда один модуль в приложении, зависит от другого модуля или окружающей среды, B. Скрытая зависимость происходит, когда A зависит от B в способ, который не очевидно. К

от Шалвы

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

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

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

let customer = Customer.find({id: 1});
// explicit — the customer has to be passed to the cartfunction Cart(customer) { this.customer = customer;}let cart = new Cart(customer);
// hidden — the cart still needs a customer,// but it doesn't say so outrightfunction Cart() { this.customer = customer; // a global variable `customer`}let cart = new Cart();

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

Оформление Пусть Cart () не будет иметь способа сказать, что объект Cart зависит от глобальной переменной клиента, за исключением случаев, когда они посмотрели на конструктор корзины.

Скрытые зависимости в дикой природе

Я поделюсь несколько примеров скрытых зависимостей, которые я сталкивался с кодовыми базами в реальном мире.

  • Выступ включают файлы

Давайте возьмем типичное приложение PHP Backend. В нашем «index.php), точка входа нашего приложения, мы могли бы иметь что-то подобное:

include 'config.php';include 'loader.php';$app = new Application($config);

Код выглядит подозрительно, не так ли? Где сделал $ config Переменная исходит? Давайте посмотрим.

включить Директива похожа на HTML PT> Теги. Он говорит интерпретателю, чтобы воспользоваться содержимым указанного файла, выполнить его и, если он имеет оператор возврата, пропустите возвращаемое значение звонящему. Это способ разделения кода через несколько файлов. Как A & LT; Script> Теги, включают также можно поставить переменные в глобальную область.

Давайте посмотрим на файлы, которые мы включаем. config.php Файл содержит типичные настройки конфигурации для приложения Backend:

$config = [  'database' => [    'host' => '127.0.0.1',    'port' => '3306',    'name' => 'home',  ],  'redis' => [    'host' => '127.0.0.1',  ]];

loader.php в основном является домашним классовым погрузчиком. Вот упрощенная версия его содержимого:

$loader = new Loader(__DIR__);$loader->configure($config);

Увидеть проблему? Код в loader.php (а остальная часть кода в index.php ) зависит от некоторой переменной имени $ config. , Но это не очевидно, где $ config определяется, пока вы не откроете config.php Отказ Этот шаблон кодирования на самом деле не редкость.

  • Включая файлы JavaScript через
    /* lots and lots of code */

    Выставка B:

    import Cart from 'cart-fx';import CartManager from 'cart-utils';
    /* lots and lots of code */
    const cart = new Cart(CartManager.default, new Customer());

    Во-вторых, очевидно, что Корзина и Cartmanager Переменные были приведены (импортированы) из Cart-Fx. и Cart-Utils модули соответственно. Во-первых, мы угадаем, как к какому модулю владеет Корзина и который владеет Cartmanager Отказ И не забывайте Клиент тоже! (Помните, наш собственный код также является модулем.)

    • Чтение из окружающей среды

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

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

    Так Почему скрытые зависимости опасны?

    Я могу придумать две основные причины:

    • Легко непреднамеренно удалить модуль зависеть без снятия зависимости Отказ Например, возьмите случай моего пакета выше. Представьте себе разработчик, создающий приложение, которое использует пакет в новой среде. При принятии решения о том, какие среды переменные переносятся из старой среды, разработчик может пренебречь, чтобы добавить те, которые необходимы по пакету, потому что они не могут найти их где угодно в кодовой базе .
    • Небольшое изменение в зависимого кода может нарушить все приложение или сделать его багги. Взять на нашем index.php Файл выше - замена первых двух строк может показаться безвредным изменением, но оно сломало бы приложение, потому что строка 2 зависит от переменной установки в строке 1. Еще более серьезным случай этого было бы что-то подобное:
    $config = […];include 'bootstrap.php';$app = new Application($config);

    Предположим, наше bootstrap.php Файл делает некоторые важные изменения в $ config. . Если по какой-то причине вторая строка перемещается в нижнюю часть, приложение запустится без броска никаких ошибок, но важная конфигурация меняется, что bootstrap.php Делает будет невидимым приложением.

    Избавление от скрытых зависимостей

    Как большая часть программного обеспечения инжиниринга, нет жестких правил для решения скрытых зависимостей, но я нашел несколько основных принципов, которые работают для меня:

    1. Написать код, который действительно модульный , не просто разделить через несколько файлов. Идеальный модуль направлен на то, чтобы быть самостоятельным и иметь минимальную зависимость от общего глобального состояния. Модуль также должен явно указывать свои зависимости.
    2. Уменьшите количество предположений, которые необходимо сделать модуль о его среде или других модулях.
    3. Выдержать четкий интерфейс. В идеале, вне таких вещей, как функция/классные подписи, пользователь вашего модуля не должен рассмотреть исходный код, чтобы выяснить, какие зависимости модуля есть.
    4. Избегайте мусорной среды. Сохраняйте искушение добавлять переменные до родительской области. Как можно чаще предпочитаю явно возвращающуюся или экспортирую переменные до вызывающего абонента.

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

    // config.phpreturn [  'database' => [    'host' => '127.0.0.1',    'port' => '3306',    'name' => 'home',  ],  'redis' => [    'host' => '127.0.0.1',  ]];

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

    // loader.php
    return function (array $config){  $loader = new Loader(__DIR__);  $loader->configure($config);}

    Положить их вместе, мы в конечном итоге с нашей index.php Файл выглядит так:

    $config = include 'config.php';(include 'loader.php')($config);
    $app = new Application($config);

    Мы можем даже пойти немного дальше, сохраняя возвращенную функцию в переменной перед его вызовом:

    $config = include 'config.php';
    $loadClasses = include 'loader.php';$loadClasses($config);
    $app = new Application($config);

    Теперь кто-нибудь смотрит на index.php можно сказать на первый взгляд, что:

    1. Файл . config.php Возвращает что-то (Мы можем догадываться, что это какой-то конфигурация, но сейчас не важно).
    2. Как файл загрузчика и Приложение зависит от этого что-то Для того, чтобы сделать их работу.

    Намного лучше, не так ли?

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

    /* lots and lots of code */

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

    Иногда вы просто не можете помочь

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

    Есть ли какие-либо опыты с скрытыми зависимостями или методами для обработки их? Делитесь в комментариях.

Оригинал: "https://www.freecodecamp.org/news/eliminating-hidden-dependencies-a95c7b03aa54/"