我什么时候应该创建服务或实用程序功能?


11

在过去的整个星期中,我一直在想这个问题:什么时候应该创建服务或实用程序功能?

在Drupal Core中,我们同时具有服务功能和实用程序功能,但是我找不到它们之间的区别(当我需要创建服务或需要创建实用程序功能时)。

我将以其中具有InternalFunctions类的Modules Weight模块为例。

<?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频道的Ken Rickard说:“如果您期望其他模块(或其他开发人员)与该代码进行交互,那么我将创建一个服务。实用程序方法只是您自己的私有快捷方式。”
Adrian Cid Almaguer

这就是我在考虑实用程序方法的想法,但是对于服务而言,有时我认为这是一个更大的考虑因素。
Adrian Cid Almaguer

我认为这很大程度上取决于类的功能以及为操作而需要提供的内容。将该Unicode类视为核心-这是静态实用程序类,而不是服务,因为它没有任何依赖关系,并且不需要维护任何状态。如果它需要服务依赖项,则DI模式将要求将其转换为服务,并且在需要时可以使用容器中的单例(或工厂生成的)实例。否则,您可以只use在需要时才使用静态类。
克莱夫(Clive)

这样,如果您期望其他模块(或其他开发人员)与该代码进行交互对我来说不是真的,那么我将创建一个服务。如果是这样的话,那Unicode将是设计提供的服务,而实际上并不需要。不要忘记,实用程序类在某些方面可以被其他模块和您自己模块中的其他代码所使用,同样容易,更轻松。但这一切都取决于您自己作为开发人员的观点/经验,这大部分都将归结为通过艰难的方式学习到的常识
Clive

2
@NoSssweat但这Unicode 一个Drupal类,仅包含静态方法!核心开发人员选择将其实现为静态类而不是服务来实现这一事实,这可能意味着您没有想到吗?实用程序类实际上并不需要被覆盖-它可以执行某些操作,如果这些不是您想要的,您可以编写自己的类。请记住,传统上属于实用程序类的事情是一次性的“我要做而没有别的”类型的方法,除了一组参数外不需要输入
Clive

Answers:


6

一般使用服务。如果可以使用静态实用程序功能,请参见以下博客文章:

所以永远不要使用静态?

好吧,没有有效的用例。一个是如果您有一个预定义的项目列表,则static可以帮助减少内存,因为它将位于类级别,而不是在任何实例中。

其他情况是不需要外部依赖项的实用程序方法,例如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')

这可能是因为实用程序类在旧式过程代码中使用。在OOP代码中,这不是必需的,在这里您应该直接注入服务。


感谢您的回答,昨天我更改了一些模块代码(因为我进行了一些提交),并创建了modules_weight服务。我有这项服务,因为它可以被其他模块使用,现在已经很通用了,您可以获取所有模块或仅获取核心模块列表。但是在模块中,此列表可能会受到show_system_modules配置变量中的值的影响,因此我制作了另一个函数,该函数采用此var然后调用服务,但是阅读您的答案后,似乎modulesList函数应该不是静态的。
阿德里安·西德·阿尔玛格

在这种情况下,您认为modulesList函数应该在服务内部还是在另一个具有依赖项注入的构造函数的类上?
阿德里安·西德·阿尔玛格

我认为您可以将其放在同一服务中,并将getModulesList()声明为受保护的方法。
4k4,15:52

但要点是,如果有人想使用getModuleList()将不可能,而modulesList()可以访问仅对模块很重要的变量。也许添加modulesList()作为另一种方法,并在使用模块配置变量的描述中添加?
阿德里安·西德·阿尔玛格

我只会公开两种方法之一。也许您可以设置默认值$force = NULL,以便您知道是否有人要使用FALSE覆盖配置值。
4k4

8

来自Slack Drupal #contribute频道的Ken Rickard说:“如果您希望其他模块(或其他开发人员)与该代码进行交互,那么我将创建一个服务。实用程序方法只是您自己的私有快捷方式。”

是的,关于服务的一个很酷的事情是任何人都可以覆盖它们。因此,如果您想让其他人可以自定义特定的代码。请参阅更改现有服务,提供动态服务

另外,如果需要对PHP单元测试进行Mock测试,则应将其作为服务。请参阅Drupal 8中的服务和依赖项注入,请参阅对更复杂的Drupal类进行单元测试

问与答:

服务单元测试

编写单元测试以查找从另一个类调用静态方法的方法


谢谢,您是否有一些参考要补充到您的答案中?
Adrian Cid Almaguer

@AdrianCidAlmaguer添加了。
没有Sssweat

1
谢谢,现在这些参考资料可以对其他用户(和我也有帮助);-)
Adrian Cid Almaguer

如果发现自己在模块的不同文件中再次使用该服务,则应创建一个服务。为什么与在同一模块中多次使用的实用工具类相比,该服务会更有用(或更好的做法)?(澄清:我不是在争论,但是具体情况似乎没有任何区别。我很想听听你为什么认为服务更有意义)
Clive

1
是的,这是一个有趣的@NoSssweat。IMO的指导原则是高于Drupal或Symfony。我认为您对代码进行了良好的,标准的,类设计,然后将结果按对该类有意义的任何方法放入当时使用的任何框架中
Clive
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.