Когда я должен создать сервис или служебную функцию?

11

На протяжении всей прошлой недели я думал об этом: когда мне следует создать сервис или служебную функцию?

В ядре Drupal у нас есть функции Services и Utility, но я не могу найти различия между ними (когда мне нужно создать сервис или когда мне нужно создать сервисную функцию).

Я возьму в качестве примера модуль веса модулей, где у меня есть класс InternalFunctions .

<?php

namespace Drupal\modules_weight\Utility;

class InternalFunctions {

  public static function prepareDelta($weight) {
    $delta = 100;

    $weight = (int) $weight;

    if ($weight > $delta) {
      return $weight;
    }

    if ($weight < -100) {
      return $weight * -1;
    }

    return $delta;
  }


  public static function modulesList($force = FALSE) {
    $modules = [];
    $installed_modules = system_get_info('module');

    $config_factory = \Drupal::service('config.factory');

    if ($force) {
      $show_system_modules = TRUE;
    }
    else {
modules.
      $show_system_modules = $config_factory->get('modules_weight.settings')->get('show_system_modules');
    }

    $modules_weight = $config_factory->get('core.extension')->get('module');

    foreach ($installed_modules as $filename => $module_info) {
      if (!isset($module_info['hidden']) && ($show_system_modules || $module_info['package'] != 'Core')) {
        $modules[$filename]['name'] = $module_info['name'];
        $modules[$filename]['description'] = $module_info['description'];
        $modules[$filename]['weight'] = $modules_weight[$filename];
        $modules[$filename]['package'] = $module_info['package'];
      }
    }
    uasort($modules, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);

    return $modules;
  }

}

В этом классе у меня есть две статические функции, но они обе являются функциями полезности или prepareDelta()являются функцией полезности и modulesList()должны быть в другом классе и иметь службу?

Единственное отличие, которое я обнаружил в это время, заключается в том, что внутри пространства имен Drupal \ Component \ Utility (где вы увидите множество функций утилит) ни один из них не использует внутри службы, и обычно служба использует другие службы внутри (я не иметь обзор всех услуг, чтобы подтвердить это).

Итак, когда я должен создать сервис или служебную функцию?

Адриан Сид Альмагуер
источник
Кен Рикард из канала Slack Drupal #contribute говорит: «Я бы создал службу, если вы ожидаете, что другие модули (или другие разработчики) будут взаимодействовать с этим кодом. Служебные методы - это всего лишь частные ярлыки для вас».
Адриан Сид
Это то, что я думал о служебных методах, но иногда я думаю, что для сервисов это более важно.
Адриан Сид
Я думаю, что многое из этого сводится к тому, что делает класс, и что ему нужно сделать доступным для работы. Возьмем Unicodeкласс в ядре - это статический служебный класс, а не сервис, потому что он не имеет никаких зависимостей и не нуждается в поддержании какого-либо состояния. Если для этого требовалась зависимость от службы, шаблон DI потребовал бы его преобразования в службу, и вы использовали бы экземпляр singleton (или сгенерированный на заводе) из контейнера, когда вам это нужно. В противном случае вы можете просто useстатический класс, когда это имеет смысл.
Клайв
Таким образом, я бы создал службу, если вы ожидаете, что другие модули (или другие разработчики) будут взаимодействовать с этим кодом , мне не соответствует. Если бы это было так, Unicodeбыл бы сервис по замыслу, и это действительно не должно быть. Не забывайте, что служебные классы могут быть также легко, в некоторых отношениях легче использоваться другими модулями и другим кодом в вашем собственном модуле. Но это все зависит от вашей собственной точки зрения / опыта разработчика, в основном это сводится к тому, что здравый смысл научился нелегко
Клайв
2
@NoSssweat Но Unicode это Drupal класс , который содержит только статические методы! Тот факт, что разработчики ядра решили реализовать его как статический класс, а не как сервис, вероятно, что-то означает, не так ли? Служебный класс на самом деле не нужно перезаписывать по своей природе - он делает некоторые вещи, если эти вещи не те, которые вы хотите, вместо этого вы пишете свой собственный класс. Помните, что вещи, которые традиционно живут в служебных классах, представляют собой однократные методы «я делаю это и ничего больше», которые не требуют ввода, кроме набора параметров
Клайв

Ответы:

6

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

Так никогда не использовать статический?

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

Другие случаи - это служебные методы, которые не требуют внешних зависимостей, например, метод slugify.

<?php
class Util
{
    public static function slug($string)
    {
        return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '_', $string)));
    }
}

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

Эти методы могут даже быть проверены модулем, так как они не требуют инициализации.

Источник: https://stovepipe.systems/post/avoiding-static-in-your-code

(Количество статического кода в Drupal теперь связано с переходом от процедурного кода D7, поэтому не используйте Drupal в текущем состоянии в качестве примера.)


О примере из вопроса, остальной части служебного класса (не показано в вопросе)

<?php

namespace Drupal\modules_weight\Utility;

/**
 * Provides module internal helper methods.
 *
 * @ingroup utility
 */
class InternalFunctions {

...

  /**
   * Return the modules list ordered by the modules weight.
   *
   * @param bool $force
   *   Force to show the core modules.
   *
   * @return array
   *   The modules list.
   */
  public static function modulesList($force = FALSE) {
    // If we don't force we need to check the configuration variable.
    if (!$force) {
      // Getting the config to know if we should show or not the core modules.
      $force = \Drupal::service('config.factory')->get('modules_weight.settings')->get('show_system_modules');
    }
    // Getting the modules list.
    $modules = \Drupal::service('modules_weight')->getModulesList($force);

    return $modules;
  }

}

вызывает собственный сервис модуля в статической оболочке:

\Drupal::service('modules_weight')

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

4k4
источник
Спасибо за ответ, вчера я немного изменил код модуля (потому что сделал несколько коммитов) и создаю сервис modules_weight. У меня есть служба, потому что она может использоваться другими модулями и теперь является общей, вы можете получить все модули или только список основных модулей. Но в модуле этот список может зависеть от значения внутри переменной конфигурации show_system_modules, поэтому я создал другую функцию, которая принимает эту переменную и затем вызывает службы, но, читая ваш ответ, кажется, что функция modulesList не должна быть статической.
Адриан Сид
В этом случае вы считаете, что функция modulesList должна находиться внутри службы или в другом классе с конструктором с внедрением зависимости?
Адриан Сид
Я думаю, что вы можете поместить его в тот же сервис и объявить getModulesList () как защищенный метод.
4k4
но дело в том, что если кто-то захочет использовать getModuleList (), это будет невозможно, а modulesList () имеет доступ к переменной, которая важна только для модуля. Может быть, добавив modulesList () в качестве другого метода и добавив в описание переменную конфигурации модуля?
Адриан Сид
Я бы сделал только один из двух методов публичным. Возможно, вы можете установить значение по умолчанию $force = NULL, чтобы вы знали, если кто-то хочет переопределить значение конфигурации с помощью FALSE.
4k4
8

Кен Рикард из канала Slack Drupal #contribute говорит: «Я бы создал службу, если вы ожидаете, что другие модули (или другие разработчики) будут взаимодействовать с этим кодом. Служебные методы - это всего лишь частные ярлыки для вас».

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

Кроме того, вы должны сделать это сервисом, если вам нужно провести пробное тестирование для PHP модульного тестирования. См. Сервисы и внедрение зависимостей в Drupal 8 , см. Модульное тестирование более сложных классов Drupal .

Q & A:

Сервисный тест

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

Нет Sssweat
источник
Спасибо, у вас есть ссылки, чтобы добавить к вашему ответу?
Адриан Сид
@AdrianCidAlmaguer добавлено.
Нет Sssweat
1
Спасибо, теперь эти ссылки могут помочь другим пользователям (и мне тоже) ;-)
Adrian Cid Almaguer
вам следует создать сервис, если вы снова используете его в разных файлах вашего модуля. Почему сервис был бы более полезен (или лучше), чем наличие класса утилит, который несколько раз используется в одном и том же модуле? (чтобы уточнить: я не спорю, но, похоже, нет особой разницы в этом контексте. Мне бы хотелось услышать, почему вы считаете, что сервис имеет больше смысла)
Клайв
1
Да, это интересно @NoSssweat. ИМО руководствуется принципами более высокого уровня, чем Drupal или Symfony. Я думаю, что вы применяете хороший, стандартный дизайн класса к своему коду, а затем вставляете результаты в любую среду, которую вы используете в данный момент, любым способом, который имеет смысл для этого класса
Клайв