Quando devo creare un servizio o una funzione di utilità?


11

Ho avuto questa domanda in mente per tutta l'ultima settimana: quando dovrei creare un servizio o una funzione di utilità?

Nel Drupal Core abbiamo entrambe le funzioni Servizi e Utilità, ma non riesco a trovare la distinzione tra loro (quando devo creare un servizio o quando devo creare una funzione di utilità).

Prenderò ad esempio il modulo Modules Weight in cui ho la classe 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;
  }

}

In questa classe ho due funzioni statiche ma sono entrambe funzioni di utilità o prepareDelta()è una funzione di utilità e modulesList()dovrebbe essere in un'altra classe e avere un servizio?

L'unica differenza che ho riscontrato in questo momento è che all'interno dello spazio dei nomi Drupal \ Component \ Utility (dove vedrai un sacco di programmi di utilità) nessuno di essi utilizza all'interno di un servizio e di solito un servizio utilizza altri servizi all'interno (io no rivedere tutti i servizi per convalidarlo).

Quindi, quando dovrei creare un servizio o una funzione di utilità?


Ken Rickard del canale #contribute di Slack Drupal dice: "Creerei un servizio se ti aspetti che altri moduli (o altri sviluppatori) interagiscano con quel codice. I metodi di utilità sono solo scorciatoie private per te".
Adrian Cid Almaguer,

Questo è quello che stavo pensando ai metodi di utilità, ma per i servizi a volte penso che sia più una considerazione.
Adrian Cid Almaguer,

Penso che molto dipenda da ciò che fa la classe e da ciò che deve averle messo a disposizione per funzionare. Prendi la Unicodeclasse nel core - questa è una classe di utilità statica, non un servizio, perché non ha dipendenze e non ha bisogno di mantenere alcuno stato. Se richiedesse una dipendenza del servizio, il modello DI richiederebbe che venisse convertito in un servizio e, quando necessario, useresti l'istanza singleton (o generata dalla fabbrica) dal contenitore. Altrimenti puoi semplicemente useclassificarlo quando ha senso.
Clive

In quanto tale, creerei un servizio se ti aspetti che altri moduli (o altri sviluppatori) interagiscano con quel codice non mi suonano fedeli. Se così fosse, Unicodesarebbe un servizio di progettazione, e non è proprio necessario. Non dimenticare che le classi di utilità possono essere usate altrettanto facilmente, più facilmente per certi aspetti, da altri moduli e altri codici nel tuo modulo. Ma tutto dipende dalla tua prospettiva / esperienza come sviluppatore, per lo più si ridurrà al buonsenso appreso nel modo più duro
Clive

2
@NoSssweat Ma Unicode è una classe Drupal che contiene solo metodi statici! Il fatto che gli sviluppatori principali abbiano scelto di implementarlo come una classe statica, piuttosto che un servizio, probabilmente significa qualcosa che non pensi? Una classe di utilità non ha davvero bisogno di essere sovrascritta, per sua natura - fa alcune cose, se quelle cose non sono quelle che vuoi, scrivi invece la tua classe. Ricorda che il tipo di cose che abitualmente vivono nelle classi di utilità sono metodi one-shot, "Faccio questo e nient'altro", che non hanno bisogno di input se non un insieme di parametri
Clive

Risposte:


6

In servizi di uso generale. Vedere il seguente post di blog quando è corretto utilizzare le funzioni dell'utilità statica:

Quindi non usare mai statico?

Bene no, ci sono casi d'uso validi. Uno è che se si dispone di un elenco di elementi predefiniti statici può aiutare a ridurre la memoria poiché sarà a livello di classe e non in nessun caso.

Altri casi sono metodi di utilità che non richiedono dipendenze esterne, ad esempio un metodo slugify.

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

Il metodo slug ha solo un comportamento molto ben definito. È facile prendere in considerazione il comportamento nei test unitari e non sarei troppo preoccupato quando vedo questa chiamata.

Questi metodi possono anche essere testati in unità poiché non richiedono l'inizializzazione.

Fonte: https://stovepipe.systems/post/avoiding-static-in-your-code

(La quantità di codice statico ora in Drupal è dovuta alla transizione dal codice D7 procedurale, quindi non usare Drupal nello stato corrente come esempio.)


Sull'esempio della domanda, il resto della classe di utilità (non mostrato nella domanda)

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

}

chiama il servizio del modulo in un wrapper statico:

\Drupal::service('modules_weight')

Ciò è probabilmente dovuto al fatto che la classe di utilità viene utilizzata nel codice procedurale legacy. Nel codice OOP questo non è necessario, qui è necessario iniettare direttamente il servizio.


Grazie per la risposta, ieri ho modificato un po 'il codice del modulo (perché ho effettuato alcuni commit) e ho creato il servizio modules_weight. Ho il servizio perché questo può essere utilizzato da altri moduli ed è ora generale, è possibile ottenere tutti i moduli o solo l'elenco dei moduli principali. Ma nel modulo questo elenco può essere influenzato dal valore all'interno della variabile di configurazione show_system_modules, quindi ho creato un'altra funzione che prende questo var e quindi chiama i servizi, ma leggendo la tua risposta sembra che la funzione modulesList non dovrebbe essere statica.
Adrian Cid Almaguer,

In questo caso pensi che la funzione modulesList dovrebbe essere all'interno del servizio o su un'altra classe con un costruttore con l'iniezione di dipendenza?
Adrian Cid Almaguer,

Penso che tu possa metterlo nello stesso servizio e dichiarare getModulesList () come metodo protetto.
4k4,

ma il punto è che se qualcuno vuole usare getModuleList () non sarà possibile e modulesList () avrà accesso a una variabile che è importante solo per il modulo. Magari aggiungendo modulesList () come altro metodo e aggiungendo nella descrizione che usa una variabile di configurazione del modulo?
Adrian Cid Almaguer,

Vorrei solo rendere pubblico uno dei due metodi. Forse puoi impostare un valore predefinito $force = NULL, in modo da sapere se qualcuno vuole sovrascrivere il valore di configurazione con un FALSO.
4k4

8

Ken Rickard del canale #contribute di Slack Drupal dice: "Creerei un servizio se ti aspetti che altri moduli (o altri sviluppatori) interagiscano con quel codice. I metodi di utilità sono solo scorciatoie private per te".

Sì, una cosa interessante dei servizi è che chiunque può sovrascriverli. Quindi, se vuoi dare ad altre persone la possibilità di personalizzare un particolare pezzo di codice. Vedi Modifica di servizi esistenti, fornitura di servizi dinamici .

Inoltre, è necessario renderlo un servizio se è necessario eseguire un test di simulazione per il test dell'unità PHP. Vedi Servizi e iniezione di dipendenze in Drupal 8 , vedi Test unitari su classi Drupal più complicate .

Domande e risposte:

Test dell'unità di servizio

Scrittura di unit test per un metodo che chiama metodi statici da un'altra classe


Grazie, hai dei riferimenti da aggiungere alla tua risposta?
Adrian Cid Almaguer,

@AdrianCidAlmaguer aggiunto.
No Sssweat,

1
Grazie, ora questi riferimenti possono aiutare gli altri utenti (e anche io) ;-)
Adrian Cid Almaguer,

dovresti creare un servizio se è qualcosa che ti vedi riutilizzare in diversi file del tuo modulo Perché un servizio sarebbe più utile (o pratica migliore) che avere una classe di utilità che viene usata più volte nello stesso modulo? (per chiarire: non sto discutendo, ma non sembra esserci alcuna differenza in quel contesto in particolare. Mi piacerebbe sapere perché pensi che un servizio abbia più senso)
Clive

1
Sì, è interessante @NoSssweat. L'IMO è guidato da principi di livello superiore rispetto a Drupal o Symfony. Penso che applichi un buon design di classe standard al tuo codice e poi inserisca i risultati in qualunque framework tu stia usando in quel momento con qualunque metodo abbia senso per quella classe
Clive
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.