什么样的设计模式用于时间测量分析器服务?

时间:2016-02-02 16:23:12

标签: php symfony design-patterns

我有一个symfony2应用程序。它抽象了一堆外部API,所有这些API都实现了ExternalApiInterface

每个ExternalApiInterface都有很多方法,例如fetchFoofetchBar

现在,我想编写一个服务来测量ExternalApiInterface实例的每次方法调用的时间。

我目前的想法是实现一个StopWatchExternalApiDecorator,它包装每个方法调用。然而,根据我的理解,这种方法导致代码重复。

我想我会使用StopWatch component进行时间测量,但这感觉很奇怪:

class StopWatchExternalApiDecorator implements ExternalApiInterface {
    public function __construct(ExternalApiInterface $api, Stopwatch $stopWatch)
    {
       $this->api = $api;
       $this->stopWatch = $stopWatch;
    }

    public function fetchBar() {
         $this->stopWatch->start('fetchBar');
         $this->api->fetchBar()
         $this->stopWatch->stop('fetchBar');
    }

    public function fetchFoo() {
         $this->stopWatch->start('fetchFoo');
         $this->api->fetchFoo()
         $this->stopWatch->stop('fetchFoo');
    }
}

好像我正在伤害DNRY(不要重复自己)的方法。我是否使用正确的模式来解决这类问题,还是有其他更合适的模式?更符合以下意义:一个地方可以进行所有测量,而且没有代码重复。

我也不喜欢触摸装饰器以防接口中有新方法。在我看来,这应该是独立的。

2 个答案:

答案 0 :(得分:0)

我正在考虑我使用的一些api,它使用一个通用函数进行调用和一个方法参数

继承人一些非常基本的伪代码

public function call($method = 'fetchBar',$params=array()){
    $this->stopWatch->start($method);
    $this->{"$method"}($params);
    $this->stopWatch->stop($method);

}

private function fetchBar(){
    echo "yo";
}

也许这有帮助

答案 1 :(得分:0)

我选择了装饰师的方法,只是在不同的层面上。

在我的架构中,api服务使用的是HttpClientInterface,最后通过调用doRequest处理了每个请求。所以,装饰者在没有代码重复的情况下最有意义:

<?php
namespace Kopernikus\BookingService\Component\Http\Client;

use Kopernikus\BookingService\Component\Performance\PerformanceEntry;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Stopwatch\Stopwatch;

/**
 * ProfileClientDecorator
 **/
class ProfileClientDecorator implements HttpClientInterface
{
    /**
     * @var Stopwatch
     */
    private $stopwatch;
    /**
     * @var HttpClientInterface
     */
    private $client;
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * ProfileClientDecorator constructor.
     * @param HttpClientInterface $client
     * @param Stopwatch           $stopwatch
     * @param LoggerInterface     $logger
     */
    public function __construct(HttpClientInterface $client, Stopwatch $stopwatch, LoggerInterface $logger)
    {
        $this->client = $client;
        $this->stopwatch = $stopwatch;
        $this->logger = $logger;
    }

    /**
     * @param RequestInterface $request
     *
     * @return ResponseInterface
     */
    public function doRequest(RequestInterface $request)
    {
        $method =  $request->getMethod();
        $response = $this->doMeasuredRequest($request, $method);
        $performance = $this->getPerformance($method);
        $this->logPerformance($performance);

        return $response;
    }

    /**
     * @param RequestInterface $request
     * @param string           $method
     *
     * @return ResponseInterface
     */
    protected function doMeasuredRequest(RequestInterface $request, $method)
    {
        $this->stopwatch->start($method);
        $response = $this->client->doRequest($request);
        $this->stopwatch->stop($method);

        return $response;
    }

    /**
     * @param $method
     * @return PerformanceEntry
     */
    protected function getPerformance($method)
    {
        $event = $this->stopwatch->getEvent($method);
        $duration = $event->getDuration();

        return new PerformanceEntry($duration, $method);
    }

    /**
     * @param PerformanceEntry $performance
     */
    protected function logPerformance(PerformanceEntry $performance)
    {
        $context = [
            'performance' => [
                'duration_in_ms'       => $performance->getDurationInMs(),
                'request_name' => $performance->getRequestName(),
            ],
        ];

        $this->logger->info(
            "The request {$performance->getRequestName()} took {$performance->getDurationInMs()} ms",
            $context
        );
    }
}

在我的services.yml

performance_client_decorator:
    class: Kopernikus\Component\Http\Client\ProfileClientDecorator
        decorates: http.guzzle_client
            arguments:
                - @performance_client_decorator.inner
                - @stopwatch
                - @logger