报告解决方案的多态性和类结构

时间:2015-09-07 13:20:54

标签: php design-patterns

我正在使用PHP编写报表生成器。这些报告将显示在网页上,通过电子邮件发送或通过其他通信线路发送。我想支持不同类型的报告数据(目前是文本和表格)。

我已经构建了这个并且它可以工作,主要类是:

  • 报表
  • ReportMailer
  • ITextReport(界面)
  • ITabularReport(界面)

我担心的是,由于我实施了ReportBuilder,我的解决方案无法扩展:

public function addTextReport(ITextReport $report)
{
    $this->textReports[] = $report;

    return $this;
}

public function addTabularReport(ITabularReport $report)
{
    $this->tabularReports[] = $report;

    return $this;
}

public function getData()
{
    $data = ['tabularReports' => [], 'textReports' => []];

    foreach($this->tabularReports as $report)
    {
        $data['tabularReports'][] = [
            'title' => $report->getTitle(),
            'keys'  => $report->getDataTitles(),
            'data'  => $report->getData(),
        ];
    }

    foreach($this->textReports as $report)
    {
        $data['textReports'][] = [
            'title' => $report->getTitle(),
            'content'   => $report->getContent(),
        ];
    }

    return $data;
}

我的问题:

如果我想添加新的报告类型(例如IGraphReport),我需要修改ReportBuilder以接受此新报告类型,然后使用ReportBuilder::getData方法解析它。这违反了开放和封闭原则,似乎错了。

我考虑过的一些解决方案:

  1. 使用IReport方法创建更通用的界面render,并将addTextReport中的addTabularReportReportBuilder替换为更通用的addReport。但是,不同的通信通道需要不同的呈现数据的方法,因此render方法以某种方式接受有关如何格式化数据的指令。我不确定这是否是最干净的解决方案。

    1. 让沟通渠道决定如何呈现报告,但后来我设想了一些if语句来检查报告的类型:if($report instanceof ITabularReport) { // handle },这会导致我“替换条件“多态”并带我回到第1点。
  2. 我不确定如何重构。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

使用addTextReportaddTabularReport方法似乎与实施逻辑联系在一起。为什么不采用addReport方法?

让每种类型的Report遵守实现getData方法的合同(接口)。即将数据返回方式的责任委托给班级。

报表

private $reports;

public function addReport(ReportContract $report)
{
    $this->reports[] = $report;
    return $this;
}


public function getData()
{
    $data = [];

    foreach($this->reports as $report) {
        $data[] = $report->getData();
    }

    return $data;
}

ReportContract

interface ReportContract
{
    public function getData();
}

ITextReport

class ITextReport implements ReportContract
{
    public function getData()
    {
        // return some data
    }
}

现在,每种新类型的报告(例如Graph)都必须实现getData方法,而基础ReportBuilder类不需要更改或重构来支持它。