Khi nào tôi nên tạo một dịch vụ hoặc một chức năng tiện ích?


11

Tôi đã có câu hỏi này trong suốt tuần qua: Khi nào tôi nên tạo một dịch vụ hoặc một chức năng tiện ích?

Trong Lõi Drupal, chúng tôi có cả hai chức năng Dịch vụ và Tiện ích, nhưng tôi không thể tìm thấy sự khác biệt giữa chúng (khi tôi cần tạo dịch vụ hoặc khi tôi cần tạo chức năng tiện ích).

Tôi sẽ lấy ví dụ mô-đun Trọng lượng mô-đun nơi tôi có lớp InternalFifts .

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

}

Trong lớp này tôi có hai hàm tĩnh nhưng chúng đều là hàm tiện ích hoặc prepareDelta()là hàm tiện ích và modulesList()nên ở trong lớp khác và có dịch vụ?

Sự khác biệt duy nhất mà tôi tìm thấy tại thời điểm này là bên trong không gian tên Drupal \ Element \ Utility (nơi bạn sẽ thấy rất nhiều tiện ích) không ai trong số họ sử dụng bên trong một dịch vụ và thường là một dịch vụ sử dụng các dịch vụ khác bên trong (Tôi không đã xem xét tất cả các dịch vụ để xác nhận điều này).

Vì vậy, khi tôi nên tạo một dịch vụ hoặc một chức năng tiện ích?


Ken Rickard từ kênh Slack Drupal #contribution nói: "Tôi sẽ tạo ra một dịch vụ nếu bạn mong đợi các mô-đun khác (hoặc nhà phát triển khác) tương tác với mã đó. Phương thức tiện ích chỉ là lối tắt riêng cho chính bạn."
Adrian Cid Almaguer

Đó là những gì tôi đã nghĩ về các phương thức Tiện ích, nhưng đối với các dịch vụ đôi khi tôi nghĩ đó là điều cần cân nhắc hơn.
Adrian Cid Almaguer

Tôi nghĩ rằng rất nhiều trong số đó thuộc về những gì lớp học làm, và những gì nó cần phải có sẵn cho nó để hoạt động. Lấy Unicodelớp trong lõi - đó là lớp tiện ích tĩnh, không phải dịch vụ, vì nó không có bất kỳ phụ thuộc nào và không cần duy trì bất kỳ trạng thái nào. Nếu nó yêu cầu một phụ thuộc dịch vụ, mẫu DI sẽ yêu cầu nó được chuyển đổi thành dịch vụ và bạn sẽ sử dụng thể hiện singleton (hoặc do nhà máy tạo ra) từ container khi bạn cần. Nếu không, bạn chỉ có thể uselớp tĩnh nó khi nó có ý nghĩa.
Clive

Như vậy, tôi sẽ tạo một dịch vụ nếu bạn mong đợi các mô-đun khác (hoặc nhà phát triển khác) tương tác với mã đó không đúng với tôi. Nếu đó là trường hợp, Unicodesẽ là một dịch vụ theo thiết kế, và nó không thực sự cần phải như vậy. Đừng quên các lớp tiện ích có thể dễ dàng, dễ dàng hơn trong một số khía cạnh, được sử dụng bởi các mô-đun khác và mã khác trong mô-đun của riêng bạn. Nhưng tất cả phụ thuộc vào quan điểm / kinh nghiệm của chính bạn với tư cách là một nhà phát triển, điều đó chủ yếu sẽ xuất phát từ lẽ thường được học theo cách khó khăn
Clive

2
@NoSssweat Nhưng Unicode một lớp Drupal chỉ chứa các phương thức tĩnh! Thực tế là các nhà phát triển cốt lõi đã chọn triển khai nó như một lớp tĩnh, thay vì một dịch vụ, có lẽ có nghĩa là điều gì đó bạn không nghĩ? Một lớp tiện ích thực sự không cần phải ghi đè, theo bản chất của nó - nó thực hiện một số thứ, nếu những thứ đó không phải là thứ bạn muốn, thay vào đó bạn viết lớp của riêng bạn. Hãy nhớ các loại phương thức truyền thống sống trong các lớp tiện ích là các kiểu phương thức "Tôi làm cái này và không có gì khác", không cần nhập liệu ngoại trừ một bộ tham số
Clive

Câu trả lời:


6

Trong dịch vụ sử dụng chung. Xem bài đăng blog sau đây khi sử dụng các chức năng tiện ích tĩnh:

Vậy không bao giờ sử dụng tĩnh?

Vâng không, có trường hợp sử dụng hợp lệ. Một là nếu bạn có một danh sách các mục được xác định trước tĩnh có thể giúp giảm bộ nhớ vì nó sẽ ở cấp độ lớp và không phải trong bất kỳ trường hợp nào.

Các trường hợp khác là các phương thức tiện ích không yêu cầu phụ thuộc bên ngoài, ví dụ như phương thức slugify.

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

Phương pháp sên chỉ thực hiện một hành vi được xác định rất tốt. Thật dễ dàng để đưa hành vi vào tài khoản trong các bài kiểm tra đơn vị và tôi sẽ không quá lo lắng khi thấy cuộc gọi này.

Các phương thức này thậm chí có thể được kiểm tra đơn vị vì chúng không yêu cầu khởi tạo.

Nguồn: https://stovepipe.systems/post/avoiding-static-in-your-code

(Số lượng mã tĩnh hiện tại trong Drupal là do quá trình chuyển đổi từ mã D7 theo thủ tục, vì vậy đừng sử dụng Drupal ở trạng thái hiện tại làm ví dụ.)


Về ví dụ từ câu hỏi, phần còn lại của lớp tiện ích (không được hiển thị trong câu hỏi)

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

}

gọi dịch vụ riêng của mô-đun trong một trình bao bọc tĩnh:

\Drupal::service('modules_weight')

Điều này có thể là do lớp tiện ích được sử dụng trong mã thủ tục kế thừa. Trong mã OOP, điều này là không cần thiết, ở đây bạn nên tiêm dịch vụ trực tiếp.


Cảm ơn câu trả lời, hôm qua tôi đã thay đổi một chút mã mô-đun (vì tôi đã thực hiện một số cam kết) và tôi tạo dịch vụ mô-đun. Tôi có dịch vụ này bởi vì các mô-đun khác có thể được sử dụng bởi các mô-đun khác và hiện tại nói chung, bạn có thể nhận được tất cả các mô-đun hoặc chỉ danh sách các mô-đun cốt lõi. Nhưng trong mô-đun, danh sách này có thể bị ảnh hưởng bởi giá trị bên trong biến cấu hình show_system_modules, vì vậy tôi đã tạo một hàm khác lấy var này và sau đó gọi các dịch vụ, nhưng đọc câu trả lời của bạn thì có vẻ như hàm moduleList không nên tĩnh.
Adrian Cid Almaguer

Trong trường hợp này, bạn có nghĩ rằng hàm moduleList nên nằm trong dịch vụ hoặc trên một lớp khác với hàm tạo với hàm phụ thuộc không?
Adrian Cid Almaguer

Tôi nghĩ bạn có thể đặt nó trong cùng một dịch vụ và khai báo getModulesList () như một phương thức được bảo vệ.
4k4

nhưng vấn đề là nếu ai đó muốn sử dụng getModuleList () sẽ không thể thực hiện được và ModuleList () có quyền truy cập vào một biến chỉ quan trọng đối với mô-đun. Có thể thêm mô-đunList () làm phương thức khác và thêm vào mô tả sử dụng biến cấu hình mô-đun?
Adrian Cid Almaguer

Tôi sẽ chỉ công khai một trong hai phương pháp. Có lẽ bạn có thể đặt giá trị mặc định $force = NULL, để bạn biết nếu ai đó muốn ghi đè giá trị cấu hình bằng FALSE.
4k4

8

Ken Rickard từ kênh Slack Drupal #contribution nói: "Tôi sẽ tạo ra một dịch vụ nếu bạn mong đợi các mô-đun khác (hoặc nhà phát triển khác) tương tác với mã đó. Phương thức tiện ích chỉ là lối tắt riêng cho chính bạn."

Vâng, một điều thú vị về dịch vụ là bất cứ ai cũng có thể ghi đè lên chúng. Vì vậy, nếu bạn muốn cung cấp cho người khác khả năng tùy chỉnh một đoạn mã cụ thể. Xem Thay đổi dịch vụ hiện có, cung cấp dịch vụ động .

Ngoài ra, bạn nên biến nó thành một dịch vụ nếu bạn cần thực hiện kiểm tra Mock để kiểm tra Đơn vị PHP. Xem Dịch vụ và tiêm phụ thuộc trong Drupal 8 , xem Đơn vị kiểm tra các lớp Drupal phức tạp hơn .

Hỏi và đáp:

Kiểm tra đơn vị dịch vụ

Viết đơn vị kiểm tra cho một phương thức gọi các phương thức tĩnh từ một lớp khác


Cảm ơn, bạn có một số tài liệu tham khảo để thêm vào câu trả lời của bạn?
Adrian Cid Almaguer

@AdrianCidAlmaguer đã thêm.
Không có Sssweat

1
Cảm ơn, bây giờ tài liệu tham khảo này có thể giúp người dùng khác (và cả tôi nữa) ;-)
Adrian Cid Almaguer

bạn nên tạo một dịch vụ nếu đó là thứ bạn thấy mình sử dụng lại trong các tệp khác nhau của mô-đun Tại sao một dịch vụ sẽ hữu ích hơn (hoặc thực hành tốt hơn) hơn là có một lớp tiện ích được sử dụng nhiều lần trong cùng một mô-đun? (để làm rõ: Tôi không tranh luận, nhưng dường như không có sự khác biệt nào trong bối cảnh đó. Tôi rất muốn nghe lý do tại sao bạn nghĩ rằng một dịch vụ có ý nghĩa hơn)
Clive

1
Vâng, đó là một điều thú vị @NoSssweat. IMO được hướng dẫn bởi các nguyên tắc cấp cao hơn so với Drupal hoặc Symfony. Tôi nghĩ rằng bạn áp dụng thiết kế lớp tốt, chuẩn, chuẩn cho mã của mình và sau đó đưa kết quả vào bất kỳ khung nào bạn đang sử dụng vào thời điểm đó bằng bất kỳ phương thức nào có ý nghĩa đối với lớp đó
Clive
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.