Wann sollte ich einen Dienst oder eine Dienstprogrammfunktion erstellen?

11

Ich hatte die ganze letzte Woche diese Frage im Kopf: Wann sollte ich einen Dienst oder eine Dienstprogrammfunktion erstellen?

Im Drupal Core haben wir sowohl Dienste- als auch Dienstprogrammfunktionen, aber ich kann keinen Unterschied zwischen ihnen finden (wenn ich einen Dienst erstellen muss oder wenn ich eine Dienstprogrammfunktion erstellen muss).

Ich werde als Beispiel das Modul Modules Weight nehmen, in dem ich die InternalFunctions- Klasse habe.

<?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 dieser Klasse habe ich zwei statische Funktionen, aber beide sind Dienstprogrammfunktionen oder prepareDelta()ist eine Dienstprogrammfunktion und modulesList()sollte in einer anderen Klasse sein und einen Dienst haben?

Der einzige Unterschied, den ich zu diesem Zeitpunkt festgestellt habe, besteht darin, dass im Namespace Drupal \ Component \ Utility (wo Sie viele Dienstprogrammfunktionen sehen) keiner von ihnen innerhalb eines Dienstes verwendet wird und normalerweise ein Dienst einen anderen Dienst darin verwendet (ich nicht habe alle Dienste überprüft, um dies zu validieren).

Wann sollte ich also einen Dienst oder eine Dienstprogrammfunktion erstellen?

Adrian Cid Almaguer
quelle
Ken Rickard vom Slack Drupal # contribute-Kanal sagt: "Ich würde einen Dienst erstellen, wenn Sie erwarten, dass andere Module (oder andere Entwickler) mit diesem Code interagieren. Dienstprogrammmethoden sind nur private Verknüpfungen für Sie."
Adrian Cid Almaguer
Das ist, was ich über Utility-Methoden nachgedacht habe, aber für Services denke ich manchmal, dass dies eher eine Überlegung ist.
Adrian Cid Almaguer
Ich denke, viel davon hängt davon ab, was die Klasse tut und was sie ihr zur Verfügung stellen muss, um operieren zu können. Nehmen Sie die UnicodeKlasse im Kern - das ist eine statische Dienstprogrammklasse, kein Dienst, da sie keine Abhängigkeiten aufweist und keinen Status beibehalten muss. Wenn eine Dienstabhängigkeit erforderlich wäre, müsste diese nach dem DI-Muster in einen Dienst konvertiert werden, und Sie würden die Singleton-Instanz (oder die werkseitig generierte Instanz) aus dem Container verwenden, wenn Sie sie benötigen. Ansonsten können Sie es nur useder statischen Klasse geben, wenn es Sinn macht.
Clive
Daher würde ich einen Dienst erstellen, wenn Sie erwarten, dass andere Module (oder andere Entwickler) mit diesem Code interagieren, was für mich nicht zutreffend ist. Wenn das der Fall Unicodewäre , wäre dies ein Service von Natur aus, und das muss nicht wirklich sein. Vergessen Sie nicht, dass Utility-Klassen genauso einfach und in gewisser Hinsicht einfacher von anderen Modulen und anderem Code in Ihrem eigenen Modul verwendet werden können. Aber das hängt alles von Ihrer eigenen Perspektive / Erfahrung als Entwickler ab. Meistens kommt es auf den gesunden Menschenverstand an, der auf die harte
Clive
2
@NoSssweat Aber Unicode ist eine Drupal-Klasse, die nur statische Methoden enthält! Die Tatsache, dass die Kernentwickler es als statische Klasse und nicht als Service implementiert haben, bedeutet wahrscheinlich etwas, was Sie nicht denken? Eine Utility-Klasse muss von Natur aus nicht wirklich überschrieben werden. Sie erledigt einige Dinge. Wenn diese Dinge nicht Ihren Wünschen entsprechen, schreiben Sie stattdessen Ihre eigene Klasse. Denken Sie daran, dass die Art von Dingen, die traditionell in Utility-Klassen leben, einmalige Methoden sind: "Ich mache das und nichts anderes", die nur eine Reihe von Parametern
eingeben müssen

Antworten:

6

Im Allgemeinen Dienste verwenden. Lesen Sie den folgenden Blog-Beitrag, wenn die Verwendung statischer Dienstprogrammfunktionen in Ordnung ist:

Also niemals statisch verwenden?

Nein, es gibt gültige Anwendungsfälle. Eine davon ist, dass statische Elemente, wenn Sie eine Liste vordefinierter Elemente haben, dazu beitragen können, den Speicher zu reduzieren, da sie sich auf Klassenebene befinden und in keinem Fall.

Andere Fälle sind Dienstprogrammmethoden, für die keine externen Abhängigkeiten erforderlich sind, z. B. eine Slugify-Methode.

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

Die Slug-Methode zeigt nur ein sehr genau definiertes Verhalten. Es ist einfach, das Verhalten bei Komponententests zu berücksichtigen, und ich würde mir keine Sorgen machen, wenn ich diesen Anruf sehe.

Diese Methoden können sogar Unit-getestet werden, da sie keine Initialisierung erfordern.

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

(Die Menge an statischem Code, die jetzt in Drupal vorhanden ist, ist auf den Übergang vom prozeduralen D7-Code zurückzuführen. Verwenden Sie Drupal daher im aktuellen Status nicht als Beispiel.)


Über das Beispiel aus der Frage, den Rest der Utility-Klasse (in der Frage nicht gezeigt)

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

}

Ruft den moduleigenen Dienst in einem statischen Wrapper auf:

\Drupal::service('modules_weight')

Dies liegt wahrscheinlich daran, dass die Utility-Klasse im alten prozeduralen Code verwendet wird. Im OOP-Code ist dies nicht erforderlich, hier sollten Sie den Dienst direkt einspeisen.

4k4
quelle
Vielen Dank für die Antwort. Gestern habe ich den Modulcode ein wenig geändert (weil ich einige Commits vorgenommen habe) und den Dienst modules_weight erstellt. Ich habe den Dienst, weil dies von anderen Modulen verwendet werden kann und jetzt allgemein ist, können Sie alle Module oder nur die Liste der Kernmodule erhalten. Im Modul kann diese Liste jedoch durch den Wert in der Konfigurationsvariablen show_system_modules beeinflusst werden. Daher habe ich eine andere Funktion erstellt, die diese Variable verwendet und dann die Dienste aufruft. Beim Lesen Ihrer Antwort sollte die Funktion modulesList jedoch nicht statisch sein.
Adrian Cid Almaguer
Denken Sie in diesem Fall, dass sich die Funktion modulesList innerhalb des Dienstes oder in einer anderen Klasse mit einem Konstruktor mit der Abhängigkeitsinjektion befinden sollte?
Adrian Cid Almaguer
Ich denke, Sie können es in den gleichen Dienst stellen und getModulesList () als geschützte Methode deklarieren.
4k4
Der Punkt ist jedoch, dass wenn jemand getModuleList () verwenden möchte, dies nicht möglich ist und modulesList () Zugriff auf eine Variable hat, die nur für das Modul wichtig ist. Fügen Sie möglicherweise modulesList () als weitere Methode hinzu und fügen Sie in der Beschreibung eine Modulkonfigurationsvariable hinzu?
Adrian Cid Almaguer
Ich würde nur eine der beiden Methoden veröffentlichen. Möglicherweise können Sie einen Standardwert festlegen $force = NULL, damit Sie wissen, ob jemand den Konfigurationswert mit FALSE überschreiben möchte.
4k4
8

Ken Rickard vom Slack Drupal # contribute-Kanal sagt: "Ich würde einen Dienst erstellen, wenn Sie erwarten, dass andere Module (oder andere Entwickler) mit diesem Code interagieren. Dienstprogrammmethoden sind nur private Verknüpfungen für Sie."

Ja, eine coole Sache an Diensten ist, dass jeder sie überschreiben kann. Wenn Sie also anderen Personen die Möglichkeit geben möchten, einen bestimmten Code anzupassen. Siehe Ändern vorhandener Dienste, Bereitstellen dynamischer Dienste .

Darüber hinaus sollten Sie es zu einem Dienst machen, wenn Sie einen Mock-Test für PHP-Unit-Tests durchführen müssen. Siehe Dienste und Abhängigkeitsinjektion in Drupal 8 , siehe Unit-Test komplizierterer Drupal-Klassen .

Fragen und Antworten:

Test der Serviceeinheit

Schreiben von Komponententests für eine Methode, die statische Methoden aus einer anderen Klasse aufruft

Kein Sssweat
quelle
Vielen Dank, haben Sie einige Referenzen, die Sie Ihrer Antwort hinzufügen können?
Adrian Cid Almaguer
@AdrianCidAlmaguer hinzugefügt.
Kein Sssweat
1
Danke, jetzt können diese Referenzen anderen Benutzern (und mir auch) helfen ;-)
Adrian Cid Almaguer
Sie sollten einen Dienst erstellen, wenn Sie ihn in verschiedenen Dateien Ihres Moduls wieder verwenden. Warum wäre ein Dienst nützlicher (oder besser zu praktizieren) als eine Dienstprogrammklasse, die mehrmals im selben Modul verwendet wird? (Zur Verdeutlichung: Ich streite nicht, aber es scheint keinen Unterschied in diesem Kontext zu geben. Ich würde gerne hören, warum Sie denken, dass ein Dienst sinnvoller ist.)
Clive
1
Ja, es ist interessant, @NoSssweat. IMO orientiert es sich an übergeordneten Prinzipien als Drupal oder Symfony. Ich denke, Sie wenden ein gutes Standard-Klassendesign auf Ihren Code an und fügen die Ergebnisse dann in das Framework ein, das Sie gerade verwenden, und zwar mit einer Methode, die für diese Klasse sinnvoll ist
Clive