In some high level package (which I don't own) I have a Route interface.
Now this Route interface contains the detail for a specific rout within my system. The high level package does not contain a method to add get
parameters to the route, like /customers?a=b
.
class CustomerRoute extends BaseRoute implements RouteInterface
{
public function __construct()
{
$this->setController(CustomerController::class);
$this->setAction('index');
$this->setUrl('/customers');
$this->setType('get');
}
}
I want to add an extra method; setGetParameters
like for example in a new base class:
public function setGetParameters(array $parameters)
{
$this->parameters = $parameters;
$this->setUrl($this->getUrl() . $this->buildQueryString($parameters));
}
protected function buildQueryString(array $parameters)
{
return '?' . http_build_query($parameters);
}
But this would violate the Liskov Substitution Principle. If I create a dependency to RouteInterface
I may not access setGetParameters
.
Like:
$route->setGetParameters([
'a'=>'b'
]);
$route->getUrl();
What is the best way to go?
I have considered:
Adapter pattern (object)
Like so?
class RouteAdapter implements Contracts\RouteAdapter
{
protected $route;
public function __construct(RouteInterface $route)
{
$this->route = $route;
}
public function setGetParameters(array $parameters)
{
$this->parameters = $parameters;
}
//the route methods
public function getUrl()
{
$this->route->getUrl() . '?' . http_build_query($this->parameters);
}
//... other methods from route
}
Decorator pattern
But with decorator (inheritance) the interface of the route is not allowed to change.
Vistor
But visitor is better used for different types of classes.
What would you do if you wouldn't own the high level package and what would you do if you did?
Would you change the base class to add the methods and also change the RouteInterface?
Update 1
Is extending the interface a solution?
This might not be a specific design pattern.
Like so:
class Route extends NewAbstractRoute
{
//...
}
abstract class RouteInterface //the original interface
{
//....
}
abstract class NewAbstractRoute extends RouteInterface
{
public function setGetParameters(array $parameters);
}
class Implementation extends NewAbstractRoute
{/*..*/}
In this way It would not violate the Liskov Substition Principle for the package and I can make a dependency to the new interface.
But this solution might not be good since it is best not to use inheritance to add methods, since it might creates too many subsystems.
controller.index(RouteInterface $route) { $route->setGetParameters(['a'=>'b']); }
This would a violation of LSP because the concrete implementation cannot be replaced by the abstraction. This is also true for a factory method with a return type:function factoryMethodExample() : RouteInterface { return new ConcreteRoute; }
. And my code would implement it as such:controller.index() { $this->factory->factoryMethodExample()->setGetParameters(['a'=>'b']); }
.ConcreteRoute::getUrl
returns something other than a URL, because then you can't use aConcreteRoute
object where aRouteInterface
is expected.