【问题标题】:Using a factory method with Inversion of control when class type is not known at runtime在运行时不知道类类型时使用具有控制反转的工厂方法
【发布时间】:2018-03-06 10:39:49
【问题描述】:

我有一个关于 IOC 的问题,当我不知道要在运行时实例化的类时。例如,我有几种类型的 View 类。 (HtmlView、CsvView、PDFView 等)实现我的视图界面。我需要的视图类的类型由用户输入(数据库中的字符串)确定。我倾向于使用具有 make 方法的 ViewFactory 类,问题是这将隐藏 View 依赖项,因为我只需要一个 ViewFactory

   class ViewFactory{
        public function make($viewType){
              if($viewType == 'html'){
                 $view = new HtmlView();
              }
              // ...
              return $view   
        }
   } 

   class ReportGenerator{
           public function constructor(Array $data, ViewFactory $viewFactory, String $viewType ){
                 $this->data = $data;
                 $this->view = $viewFactory($viewType);
           }
           public function generate(){
                  return $this->view->render($this->data)
           }
   }   

在我看来,ReportGenerator 依赖于基础 ViewInterface 似乎并不清楚。有没有更好的方法,不使用静态方法。

【问题讨论】:

  • 不要以$viewType的形式传入字符串,传入视图的typehinted interface。尽管您使用 viewFactory 来创建视图本身,这显然意味着 ReportGenerator 不处理实际选择应该实例化的类,但将 viewFactory 放在具体类中感觉很奇怪。我会尽快写这个作为答案。

标签: php oop design-patterns dependency-injection inversion-of-control


【解决方案1】:
interface ViewInterface {

  public function render();
}

class HtmlView implements ViewInterface {

  const HTML_VIEW = 'html';

  public function render() {
    // TODO: Implement render() method.
  }
}

class AnotherView implements ViewInterface {

  const ANOTHER_VIEW = 'another';

  public function render() {
    // TODO: Implement render() method.
  }

}

class ViewFactory {

  public static function make($viewType) { // this could also be static
    // or use a construct, whichever strikes your fancy
    if ('html' == $viewType) { // fan of Yoda metod, I am
      $view = new HtmlView();
    }
    // ...
    return $view;
  }
}

class ReportGenerator {

  private $data;
  private $view;

  public function constructor(array $data, ViewInterface $view) {
    $this->data = $data;
    $this->view = $view;
  }

  public function generate() {
    return $this->view->render($this->data);

  }
}


$view = ViewFactory::make(HtmlView::HTML_VIEW);


$report = new ReportGenerator([], $view);

不要让 ReportGenerator 处理任何与视图相关的事情,它绝对不能这样做,只需传入已经创建的视图即可。

视图本身应该在 ReportGenerator 之外创建(或与此相关的任何其他类 - 除了 ViewFactory)。

在 ViewFactory 中你总是可以添加其他方法来处理创建新视图的逻辑。

【讨论】:

  • “或任何其他部分”这就是棘手的部分,不是吗。你建议把它放在哪里?它需要知道要创建哪种类型的视图,并且该知识来自用户输入,因此我想您必须将其放置在控制器中。如果这是请求范围,您可以在引导期间将其放在前端控制器中,但情况并非如此。
  • 我建议放在需要的地方。由于 ViewFactory 负责实际逻辑,因此没有违反任何原则。一切都被隔离在自己的小世界中。
  • 你是对的,这是一个人为的例子。实际上有一个ReportProcessor 类,它从队列中读取ReportsReportProcessor 类基于Report 中的数据创建View 类。您上面的代码都将在 ReportProcessor::handle(Report $report) 方法中。然后我需要将ViewFactory DI 到ReportProcessor,但这又隐藏了对View* 的依赖。
  • @ishegg @Andrew 是的,这是我的问题。这根本不是从控制器运行的,而是从 JobProcessor 运行的,并且 JobProcessor 需要有一个 View* 类来呈现报告。如果我 DI 一个 ViewFactory JobProcessorView 的依赖仍然不是 100% 明确的,但比“新”一个 View 类更好。
  • 哦,我知道你打算用这个去哪里了。是的,这改变了一些事情。
猜你喜欢
  • 2015-05-15
  • 1970-01-01
  • 2020-05-12
  • 2011-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 2012-01-13
相关资源
最近更新 更多