我有一个带有搜索功能的界面:
interface Searcher
{
public function search($text,$limit)
}我有一些基于API的实现
class APISeach implements Searcher
{
public function search($text,$limit)
{
$params = [
'name' => $sName,
'maxRows' => $iLimit,
];
$response = Http::get('http://some_api_service/search', $params)->json();
}
}我有一些使用这个搜索的代码:
class MyController extends Controller
{
private $seacher;
public function __construct(Searcher $mySeacher)
{
$this->seacher = $mySeacher;
}
public function search($text,$limit=10)
{
$this->seacher->search($text,$limit)
}
}一切看起来都很好(可能是这样)。但是,如果我需要改变API实现,它将需要另一个参数。例如:
class AnotherAPISeach implements Searcher
{
public function search($text,$language,$fuzzy,$limit)
{
$ApiObjectFromLib = new ApiObjectFromLib();
$response = $ApiObjectFromLib->search($text,$language,$fuzzy,$limit)->json();
}
}所以它不能再实现Searcher接口了。有没有为API函数使用接口的方法?所有的API都需要不同的参数,如果我需要为每个API更改接口或控制器代码,这是不好的。
发布于 2021-06-30 21:44:33
您可以使用variadic arguments
interface Searcher
{
public function search($text, $limit, ...$additional);
}如果这与interface的目的背道而驰,则由您来决定?
发布于 2021-07-01 23:40:43
像这样(demo)怎么样?
public function search(string $name, int $limit = \Search\Limit::DEFAULT)
{
return $this->api->search(
new \Search\Name($name),
new \Search\Limit($limit)
);
}
public function lookup(
string $name,
string $language = \Search\Language::DEFAULT,
bool $fuzzy = \Search\Fuzzy::DEFAULT,
int $limit = \Search\Limit::DEFAULT
) {
return $this->api->search(
new \Search\Name($name),
new \Search\Language($language),
new \Search\Fuzzy($fuzzy),
new \Search\Limit($limit)
);
}这些“规范”看起来像这样:
namespace Search
{
interface Specification
{
public function __invoke(array $params): array;
}
class Name implements Specification
{
private $name = null;
public function __construct(string $name)
{
$this->name = $name;
}
public function __invoke(array $params): array
{
return [
'name' => $this->name,
];
}
}
class Language implements Specification
{
const DEFAULT = 'en';
private $language = null;
public function __construct(string $language)
{
$this->language = $language;
}
public function __invoke(array $params): array
{
return [
'language' => $this->language ?? 'en',
];
}
}
class Fuzzy implements Specification
{
const DEFAULT = true;
private $fuzzy = null;
public function __construct(bool $fuzzy)
{
$this->fuzzy = $fuzzy;
}
public function __invoke(array $params): array
{
return [
'fuzzy' => $this->fuzzy,
];
}
}
class Limit implements Specification
{
const DEFAULT = 10;
private $max = null;
public function __construct(int $limit)
{
$this->limit = $limit;
}
public function __invoke(array $params): array
{
return [
'maxRows' => $this->limit ?: self::DEFAULT,
];
}
}
}可搜索API的组成如下所示:
interface Searchable
{
public function search(\Search\Specification... $criteria);
}
class Search implements Searchable
{
private $url = '/search';
private $defaults = [
'maxRows' => \Search\Limit::DEFAULT,
];
public function search(\Search\Specification ...$criteria)
{
return \Http::get($this->url, array_reduce(
$criteria,
fn($params, $criteria) => $criteria($params) + $params,
$this->defaults
))->json();
}
}Specification Pattern很有趣,因为它意味着进入域的请求实际上只是一系列决策,这些决策导致可以在其他地方应用的配置。
例如,请注意上面的$criteria($params)对象被赋予了请求的当前$params,对于该请求,它可以覆盖参数、读取和修改参数,或者潜在地结合规范检查来验证参数。
关于array + array语法的说明,这是一种合并数组的方法:
['foo' => 'bar'] + ['foo' => 'baz'] // left takes precedence: ['foo' => 'bar']Filter/Criteria非常相似;我倾向于认为那些与应用它的对象(Repository、Query或Collection)的链接比Specification更紧密,在我看来,Specification更多地应用于要返回的内容。
https://stackoverflow.com/questions/68195905
复制相似问题