我有一个带有搜索功能的界面:
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的接口或控制器代码,那就不好了。
您可以使用可变参数
interface Searcher
{
public function search($text, $limit, ...$additional);
}
如果这违背了interface
的目的,则由您决定
这样的东西(演示)怎么样?
public function search(string $name, int $limit = SearchLimit::DEFAULT)
{
return $this->api->search(
new SearchName($name),
new SearchLimit($limit)
);
}
public function lookup(
string $name,
string $language = SearchLanguage::DEFAULT,
bool $fuzzy = SearchFuzzy::DEFAULT,
int $limit = SearchLimit::DEFAULT
) {
return $this->api->search(
new SearchName($name),
new SearchLanguage($language),
new SearchFuzzy($fuzzy),
new SearchLimit($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(SearchSpecification... $criteria);
}
class Search implements Searchable
{
private $url = '/search';
private $defaults = [
'maxRows' => SearchLimit::DEFAULT,
];
public function search(SearchSpecification ...$criteria)
{
return Http::get($this->url, array_reduce(
$criteria,
fn($params, $criteria) => $criteria($params) + $params,
$this->defaults
))->json();
}
}
规范模式很有趣,因为它意味着进入域的请求实际上只是一系列决策,这些决策导致了可以在其他地方应用的配置。
例如,请注意,上面的$criteria($params)
对象中的每个对象都被赋予了请求的当前$params
,它可以覆盖参数、读取和修改参数,或者可能包含规范检查来验证参数。
注意array + array
语法,这是一种合并数组的方法:
['foo' => 'bar'] + ['foo' => 'baz'] // left takes precedence: ['foo' => 'bar']
过滤器/标准非常相似;我倾向于认为那些与它所应用的对象(Repository、Query或Collection)的链接比Specification更紧密,在我看来,Specification更适用于要返回的内容。