如何重构自定义 http 客户端 class?

How to refactor custom http client class?

我的项目中有一个用于 http 客户端 class 的 class 我需要添加日志和持续时间。

这是class:

class RestClient {

public static function get($provider, $step, $url, $headers = [], $data = [])
{
    return self::request(
        Http::acceptJson()->withHeaders($headers),
        'get',
        $url,
        $provider,
        $step,
        $data
    );
 }

 private static function request($pendingRequest, $method, $url, $provider, $step, $data = [])
 {
    $startTime = microtime(true);
    $client = $pendingRequest->{$method}($url, $data);
    $durationTime = microtime(true) - $startTime;

    if ($client->clientError() || $client->serverError()) {
        HttpLog::addErrorLog(
            $provider, $step, $data, $client->toException()->getMessage(), $url, $durationTime
        );

        if ($client->status() == 401) {
            throw new InvalidTokenException();
        }

        if ($client->serverError()) {
            throw new ServerException();
        }

        throw new ProviderException('ProviderException');
     }

    HttpLog::addLog(
        $provider, $step, $client, $url, $durationTime
    );

    return $client->json();
 }
}

我在项目的多个位置调用了 RestClient get 方法和其他方法。

调用示例:

return RestClient::get(
            $this->providerName(),
            'INQUIRY',
            {$this->configs['base_url'],
            ['Authorization' => "Bearer $token",]
        );

是否需要重构 RestClient class?

我建议减少参数的数量。 考虑使用链接而不是静态方法。

示例:

$restClient = new RestClient;
$restClient->provider($this->providerName());
$restClient->step("INQUIRY");
$restClient->url($this->configs['base_url']);
$restClient->headers(['Authorization' => "Bearer $token",]);
$restClient->get();

我认为链接使用起来会更方便

不知道你所说的“重构”到底是什么意思。 但如果你问的是扩展现有服务行为的最佳方式,我会说它是模式装饰器。但它需要摆脱静态方法。 此外,最好将 RestClient 注册为 DI 容器中的服务(如果您使用的是 Lumen,这里是手册 https://lumen.laravel.com/docs/5.6/container

结果代码可能如下所示:

interface RestClientInterface
{
    public function get(string $url, array $data): array;
    public function post(string $url, array $data): array;
}

class RestClient implements RestClientInterface
{
    private $provider;

    public function __construct(ProviderInterface $provider) {
        $this->provider = $provider;
    }

    public function get(string $url, array $data): array {
        // do actual GET request using provider and return response, no additional actions
    }
    public function post(string $url, array $data): array {
       // do actual POST request using provider and return response, no additional actions
    }
}

class LoggingClient implements RestClientInterface
{
    private $decorated;

    public function __construct(RestClientInterface $decorated) {
       $this->decorated = $decorated;
    }

    public function get(string $url, array $data): array {
        $result = $this->decorated->get($url, $data);
        // here do additional actions, logging etc
        return $result;
    }

    public function post(string $url, array $data): array {
        $result = $this->decorated->get($url, $data);
        // here do additional actions, logging etc
        return $result;
    }
}

然后在容器中注册客户端实现并使用所需的装饰器对其进行扩展:

$this->app->bind(RestClient::class, function ($app) {
    return new RestClient(new MyProvider());
});

$this->app->extend(RestClient::class, function ($decoratedClient, $app) {
    return new LoggingClient($decoratedClient);
});

然后在代码中获取客户端实例:

$client = $this->app->make(RestClient::class); // or App::make(RestClient::class)

通过这种方法,您可以将服务包装在多个装饰器中,每个装饰器将负责执行一项附加操作。它提供了更大的灵活性。

$service = new DecoratorOne(new DecoratorTwo(new BasicImplementation()));