我有一个symfony2应用程序。它抽象了一堆外部API,所有这些API都实现了ExternalApiInterface
。
每个ExternalApiInterface
都有很多方法,例如fetchFoo
和fetchBar
。
现在,我想编写一个服务来测量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(不要重复自己)的方法。我是否使用正确的模式来解决这类问题,还是有其他更合适的模式?更符合以下意义:一个地方可以进行所有测量,而且没有代码重复。
我也不喜欢触摸装饰器以防接口中有新方法。在我看来,这应该是独立的。
答案 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