单例和服务定位器的真正替代方案是什么?

4
我正在努力提升编程技能,遇到了一个令人沮丧的问题,最好通过以下示例进行解释:
假设我正在使用PHP创建一个微型CMS。这个微型CMS有一个路由器类,负责路由。它还保存从URI中提取的额外参数。
class Router{
  private $uri;
  private $params;
  ...
  public function getRoute(){ ... }
  ...
  public function getParams(){
    return $this->params;
  }
  ...
}

我还有一个前端控制器,我向其中传递了一个新的路由器(Router()对象)。到目前为止,一切都很好,我可以通过 $router->getParams(); 方法在我的前端控制器中访问额外的参数。

class FrontController{
  private $controller;
  private $view;

  public function __construct(Router $router){
    $route = $router->getRoute();
    ...
    $params = $router->getParams(); //Yay, I can get to the params here!
    ...
    $this->view = new View($route->getModel());
    ...
  }

现在对我来说变得复杂的是,这个前端控制器构建了一个视图。我希望这个视图也能够访问路由器的函数(例如,能够从中获取URI参数)。
class View{
  public function output(){
    //But how do I access the Router's params here...?
  }
}

首先,最简单的解决方案似乎是将Router变成单例或只是将函数设为静态,并简单地调用Router::getParams()...但这是反模式。
其次,明显的解决方案是将我的Router实例传递给View的构造函数。我想避免这种情况,因为我担心在某个时候我的构造函数会变得很大。我不确定还需要像此类视图这样访问多少其他类,而我不希望它们不必要地 clutter 构造函数。这种担忧是否正确?
另一种解决方案是使用服务定位器,在我的View中调用类似于$serviceLocator->getRouter()的内容。但那也是一种反模式。
那么解决方案是什么?还是我的CMS架构存在根本性问题?
1个回答

2

FrontController使用依赖注入来处理问题,这被认为是处理此类问题的最佳方式。因为你直接传递了类实例,所以不会出现单例中固有的类似于global的问题。

担心你的View构造函数会变得臃肿是没有必要的。如果你需要一个依赖项,则需要在某个地方注入它。你不必在构造函数中进行此操作。你可以像下面这样创建一个函数:

public function setRouter(Router $router) {
     $this->router = $router;
}

像这样注入它。

如果你的类本身变得非常臃肿,那么你需要将其重构为子类。


将路由器依赖项通过多个实例化级别注入(首先注入到前端控制器,然后注入到视图中,然后可能需要在其他地方注入...)是否不被认为是不良实践?何时知道该将类重构为子类? 什么时候有太多的依赖关系? - Daniel Frąk
1
不完全是这样。您正在注入相同的类,因此需要注入多次。我的经验法则是,如果一个类超过700行,则需要进行重构。至于依赖项过多...这取决于您正在做什么。 - Machavity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接