Принцип разделения интерфейса: как разделить большой интерфейс с помощью множества дополнительных методов
Допустим, у меня есть интерфейс:
interface WorkerInterface
{
public function doCommonAction1(CommonAction1Params $params): CommonAction1Result;
public function doCommonAction2(CommonAction2Params $params): CommonAction2Result;
/**
* @return void
*
* @throws UnsupportedMethodException
*/
public function doSpecificAction1(SpecificAction1Params $params): SpecificAction1Result;
/**
* @return void
*
* @throws UnsupportedMethodException
*/
public function doSpecificAction2(SpecificAction2Params $params): SpecificAction2Result;
}
Проблема в том, что методы
doSpecificAction1
и
doSpecificAction2
являются необязательными и поддерживаются не всеми работниками. Работник может поддержать
doCommonAction1
и
doCommonAction2
только, а также
doCommonAction1
,
doCommonAction2
и
doSpecificAction1
, или же
doCommonAction1
,
doCommonAction2
,
doSpecificAction2
, или все методы вместе.
Также у меня есть WorkerFactory:
class WorkerFactory
{
public function createWorker(string $workerId): WorkerInterface
{
// worker is created here
}
}
Тогда у меня есть контроллер:
class ActionController {
public function commonAction1(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
$worker->doCommonAction1(new CommonAction1Params());
}
public function commonAction2(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
$worker->doCommonAction2(new CommonAction2Params());
}
public function specificAction1(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
try {
$worker->doSpecificAction1(new SpecificAction1Params());
} catch (UnsupportedMethodException $e) {
// do something
}
}
public function specificAction2(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
try {
$worker->doSpecificAction2(new SpecificAction2Params());
} catch (UnsupportedMethodException $e) {
// do something
}
}
}
Очевидно, что теперь мой код нарушает принцип разделения интерфейса. Я бы хотел его реорганизовать. Хорошо, я пытаюсь сделать что-то вроде этого:
interface WorkerInterface
{
public function doCommonAction1(CommonAction1Params $params): CommonAction1Result;
public function doCommonAction2(CommonAction2Params $params): CommonAction2Result;
}
interface SpecificAction1AwareInterface
{
public function doSpecificAction1(SpecificAction1Params $params): SpecificAction1Result;
}
interface SpecificAction2AwareInterface
{
public function doSpecificAction2(SpecificAction2Params $params): SpecificAction2Result;
}
Итак, теперь мои рабочие будут выглядеть так:
class Worker1 implements WorkerInterface {}
class Worker2 implements WorkerInterface, SpecificAction1AwareInterface {}
class Worker3 implements WorkerInterface, SpecificAction1AwareInterface, SpecificAction2AwareInterface {}
И теперь мой контроллер меняется на это:
class ActionController {
public function commonAction1(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
$worker->doCommonAction1(new CommonAction1Params());
}
public function commonAction2(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
$worker->doCommonAction2(new CommonAction2Params());
}
public function specificAction1(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
if ($worker instanceof SpecificAction1AwareInterface) {
$worker->doSpecificAction1(new SpecificAction1Params());
} else {
// do something
}
}
public function specificAction2(string $workerId, WorkerFactory $factory)
{
$worker = $factory->createWorker($workerId);
if ($worker instanceof SpecificAction2AwareInterface) {
$worker->doSpecificAction1(new SpecificAction2Params());
} else {
// do something
}
}
}
Но я думаю, что этот код кажется некрасивым. Я не уверен, что использование instanceof - хорошая идея, особенно потому, что
SpecificAction1AwareInterface
и
SpecificAction2AwareInterface
не связаны с
WorkerInterface
вообще.
Так есть ли какие-нибудь шаблоны проектирования, подходящие для моей ситуации? Заранее спасибо.