Kiedy powinienem utworzyć usługę lub funkcję narzędziową?

11

Przez cały tydzień myślałem o tym: kiedy powinienem stworzyć usługę lub funkcję użyteczności?

W Drupal Core mamy zarówno funkcje Usługi, jak i Narzędzie, ale nie mogę znaleźć rozróżnienia między nimi (kiedy muszę utworzyć usługę lub kiedy muszę utworzyć funkcję narzędzia).

Jako przykład wezmę moduł Moduły wagi , w którym mam klasę 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;
  }

}

W tej klasie mam dwie funkcje statyczne, ale obie są funkcjami użytecznymi, czy też prepareDelta()są funkcjami użytecznymi i modulesList()powinny być w innej klasie i mieć usługę?

Jedyną różnicą, którą znalazłem w tym momencie, jest to, że w przestrzeni nazw Drupal \ Component \ Utility (gdzie zobaczysz wiele funkcji narzędzi) żadna z nich nie korzysta z usługi i zwykle usługa korzysta z innych usług w środku (ja nie przejrzyj wszystkie usługi, aby to potwierdzić).

Kiedy więc powinienem utworzyć usługę lub funkcję narzędziową?

Adrian Cid Almaguer
źródło
Ken Rickard z kanału Slack Drupal #contribute mówi: „Stworzyłbym usługę, jeśli oczekujesz, że inne moduły (lub inni programiści) będą współpracować z tym kodem. Metody użytkowe to po prostu prywatne skróty”.
Adrian Cid Almaguer
Właśnie to myślałem o metodach użytkowych, ale czasem w przypadku usług myślę, że to bardziej kwestia.
Adrian Cid Almaguer
Myślę, że wiele z tego sprowadza się do tego, co robi klasa i co musi udostępnić, aby działać. Weź Unicodeklasę w rdzeniu - jest to statyczna klasa użyteczności, a nie usługa, ponieważ nie ma żadnych zależności i nie musi utrzymywać żadnego stanu. Jeśli wymagałaby zależności usługowej, wzorzec DI wymagałby przekształcenia go w usługę i używałbyś instancji singletonu (lub generowanej fabrycznie) z kontenera, gdy byłeś potrzebny. W przeciwnym razie możesz po prostu useklasę statyczną, gdy ma to sens.
Clive
Jako taki, stworzyłbym usługę, jeśli spodziewasz się, że inne moduły (lub inni programiści) będą współpracować z tym kodem, że nie brzmi to dla mnie. Gdyby tak było, Unicodebyłaby to usługa z założenia i tak naprawdę nie musi być. Nie zapominaj, że klasy narzędzi mogą być równie łatwo, pod pewnymi względami, używane przez inne moduły i inny kod we własnym module. Ale to wszystko zależy od twojej perspektywy / doświadczenia jako programisty, głównie sprowadza się to do zdrowego rozsądku, którego nauczyłeś się na własnej skórze
Clive
2
@NoSssweat But Unicode to klasa Drupal, która zawiera tylko metody statyczne! Fakt, że rdzeniowcy zdecydowali się na implementację go jako klasy statycznej, a nie usługi, prawdopodobnie oznacza coś, co nie sądzisz? Klasa użyteczności tak naprawdę nie musi być nadpisywana ze swojej natury - robi pewne rzeczy, jeśli te rzeczy nie są tym, czego chcesz, zamiast tego piszesz własną klasę. Pamiętaj, że rzeczy, które tradycyjnie żyją w klasach użyteczności, to jednorazowe metody typu „robię to i nic więcej”, które nie wymagają wprowadzania danych oprócz zestawu parametrów
Clive

Odpowiedzi:

6

W ogólnym użyciu usługi. Zobacz następujący post na blogu, gdy korzystanie ze statycznych funkcji narzędzia jest w porządku:

Więc nigdy nie używaj statycznego?

Cóż, nie, istnieją ważne przypadki użycia. Jednym z nich jest to, że jeśli masz listę predefiniowanych elementów, static może pomóc zmniejszyć pamięć, ponieważ będzie ona na poziomie klasy, a nie w żadnym przypadku.

Inne przypadki to metody narzędziowe, które nie wymagają zewnętrznych zależności, na przykład metoda slugify.

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

Metoda ślimaka ma tylko bardzo dobrze zdefiniowane zachowanie. Łatwo jest wziąć to pod uwagę w testach jednostkowych i nie martwiłbym się zbytnio, gdy widzę to wezwanie.

Te metody mogą być nawet testowane jednostkowo, ponieważ nie wymagają inicjalizacji.

Źródło: https://stovepipe.systems/post/avoiding-static-in-your-code

(Ilość kodu statycznego teraz w Drupal wynika z przejścia od kodu D7 proceduralnego, więc nie używaj Drupala w obecnym stanie jako przykładu.)


O przykładzie z pytania, reszcie klasy użyteczności (nie pokazanej w pytaniu)

<?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;
  }

}

wywołuje własną usługę modułu w statycznym opakowaniu:

\Drupal::service('modules_weight')

Jest tak prawdopodobnie dlatego, że klasa użyteczności jest używana w starszym kodzie proceduralnym. W kodzie OOP nie jest to konieczne, tutaj należy wstrzyknąć usługę bezpośrednio.

4k4
źródło
Dzięki za odpowiedź, wczoraj trochę zmieniłem kod modułu (ponieważ dokonałem kilku zmian) i tworzę usługę moduły_waga. Mam tę usługę, ponieważ mogą z niej korzystać inne moduły, a teraz jest ogólna, możesz pobrać wszystkie moduły lub tylko listę modułów podstawowych. Ale w module na tę listę może mieć wpływ wartość wewnątrz zmiennej konfiguracyjnej show_system_modules, więc stworzyłem inną funkcję, która przyjmuje tę zmienną, a następnie wywołuję usługi, ale czytając twoją odpowiedź, wydaje się, że funkcja moduleList nie powinna być statyczna.
Adrian Cid Almaguer
Czy w takim przypadku uważasz, że funkcja moduleList powinna znajdować się w usłudze lub w innej klasie z konstruktorem z wstrzykiwaniem zależności?
Adrian Cid Almaguer
Myślę, że możesz umieścić go w tej samej usłudze i zadeklarować getModulesList () jako metodę chronioną.
4k4
ale chodzi o to, że jeśli ktoś chce użyć getModuleList (), nie będzie to możliwe, a moduły ModuleList () mają dostęp do zmiennej, która jest ważna tylko dla modułu. Może dodanie modułów moduleList () jako kolejnej metody i dodanie opisu wykorzystującego zmienną konfiguracji modułu?
Adrian Cid Almaguer
Podałbym tylko jedną z dwóch metod do wiadomości publicznej. Być może możesz ustawić wartość domyślną $force = NULL, abyś wiedział, czy ktoś chce zastąpić wartość konfiguracji FALSE.
4k4
8

Ken Rickard z kanału Slack Drupal #contribute mówi: „Stworzyłbym usługę, jeśli oczekujesz, że inne moduły (lub inni programiści) będą współpracować z tym kodem. Metody użytkowe to po prostu prywatne skróty”.

Tak, fajną rzeczą w usługach jest to, że każdy może je zastąpić. Więc jeśli chcesz dać innym ludziom możliwość dostosowania określonego fragmentu kodu. Zobacz Zmiana istniejących usług, dostarczanie usług dynamicznych .

Ponadto powinieneś uczynić go usługą, jeśli chcesz wykonać próbny test do testowania jednostek PHP. Zobacz Usługi i wstrzykiwanie zależności w Drupal 8 , zobacz Testowanie jednostek bardziej skomplikowanych klas Drupal .

Pytania i odpowiedzi:

Test jednostki serwisowej

Pisanie testów jednostkowych dla metody wywołującej metody statyczne z innej klasy

Bez sssweat
źródło
Dzięki, czy masz jakieś odniesienia do dodania do swojej odpowiedzi?
Adrian Cid Almaguer
Dodano @AdrianCidAlmaguer.
Bez Sssweat
1
Dzięki, teraz te referencje mogą pomóc innym użytkownikom (i mnie też) ;-)
Adrian Cid Almaguer
powinieneś stworzyć usługę, jeśli jest to coś, czego używasz ponownie w różnych plikach modułu. Dlaczego usługa byłaby bardziej przydatna (lub lepsza praktyka) niż posiadanie klasy narzędzi, która jest używana kilka razy w tym samym module? (aby wyjaśnić: nie kłócę się, ale wydaje się, że nie ma żadnej różnicy w tym kontekście. Chciałbym usłyszeć, dlaczego według ciebie usługa ma większy sens)
Clive
1
Tak, to jest interesujące @NoSssweat. IMO kieruje się zasadami wyższego poziomu niż Drupal lub Symfony. Myślę, że stosujesz dobry, standardowy projekt klasy w swoim kodzie, a następnie umieszczasz wyniki w dowolnym frameworku, którego używasz w tym czasie, dowolną metodą, która ma sens dla tej klasy
Clive