我该如何解决这个 OOP 设计问题?

发布于 2024-10-12 13:58:05 字数 753 浏览 2 评论 0原文

我有一个包含 3 个组件的系统:

报告 - 包含用于定义报告在其输出中实际包含的内容的逻辑。示例包括 TopMerchantsReportLowestTransactionsReport

ReportRunner - 由于报告仅进行数据收集和生成,因此该类负责运行所有报告并发送结果(例如通过电子邮件)。每个Report都有自己的ReportRunner

ReportProfile - 数据库表的对象镜像,其中包含特定报告的用户设置。


报告可以具有多种可插入行为,例如可消化和/或可调度。由于 PHP 没有 mixin,因此最好用装饰器模式来表示。

我的实际问题是,当关联的 Report 用诸如 Schedulable 之类的内容装饰时,这 3 个对象中的每一个都需要修改其行为。例如,ReportRunner 现在只需要收集计划的报告,而 ReportProfile 将受益于 isScheduled() 方法。

我不想强迫用户必须装饰所有 3 个类。这不仅容易出错,而且我还必须为每个行为创建 3 个装饰器(每个类一个)。还有什么其他解决方案?

I have a system that has 3 components:

Report - Contains logic for defining what a report actually contains in its output. Examples include TopMerchantsReport and LowestTransactionsReport.

ReportRunner - As the reports only do data gathering and generating, this class is responsible for running all of the reports and dispatching the results (via email, for example). Each Report has its own ReportRunner.

ReportProfile - Object mirror of a database table that contains a user's settings for a specific report.


A Report can have multiple pluggable behaviors, such as being Digestable and/or being Schedulable. Since PHP has no mixins, this is best represented by the decorator pattern.

My actual problem is that each of these 3 objects needs its behavior modified when the associated Report is decorated with something like Schedulable. For example, the ReportRunner now needs to only gather scheduled reports, and ReportProfile would benefit from a isScheduled() method.

I don't want to force a user to have to decorate all 3 classes. Not only is that error prone, but then I'd have to create 3 decorators for each behavior (one for each class). What other solution is there?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

通知家属抬走 2024-10-19 13:58:05

如果您想要一个具体的类,例如报告其他两个组件中的不同行为,您最终必须装饰它们,所以您无法避免这一点。唯一的问题可能是管理这三个组件的创建,以根据选择的行为(例如可消化)进行装饰。这可以使用工厂方法模式来完成,它允许您创建所有这三个组件的适当类型。

If you want to have for a concrete class of e.g. Report different behaviour in other two components, you have to eventually decorate them, so you cannot avoid that. The only problem migth be managing creation of those three components to be decorated according to choosen behaviour e.g. Digestable. This can be done using a factory method pattern, which allows you to create appropriate kind of all those three components.

醉态萌生 2024-10-19 13:58:05
class Report{
    const SCHEDULED = 1;  
    const DIGESTABLE = 2;  

    private $behaviour;  

    public function getReportData(){}  
    public function getReportProfile(){}  
    public function getBehaviour(){
        return $this->behaviour;
    }
}  

class ReportRunner{  
    public function runReport(){  
        switch($this->report->getBehaviour()){  
            case Report::SCHEDULED
            //do sheduled stuff  
            case Report::DIGESTABLE
            //do DIGESTABLE stuff  
        }  
    }  
}
class Report{
    const SCHEDULED = 1;  
    const DIGESTABLE = 2;  

    private $behaviour;  

    public function getReportData(){}  
    public function getReportProfile(){}  
    public function getBehaviour(){
        return $this->behaviour;
    }
}  

class ReportRunner{  
    public function runReport(){  
        switch($this->report->getBehaviour()){  
            case Report::SCHEDULED
            //do sheduled stuff  
            case Report::DIGESTABLE
            //do DIGESTABLE stuff  
        }  
    }  
}
乙白 2024-10-19 13:58:05

如果我正确理解你的痛苦,我唯一能想到的就是使用工厂设计。工厂有责任根据其装饰提供与特定报表相对应的 ReportRunner 和 ReportProfile。

因此,每个 ReportRunner 都有一个类,ReportProfile 也有一个类,非常相似。

ReportRunner Factory 应该有一个方法,该方法接受报表的装饰并返回它的 ReportRunner,因此您可以执行以下操作:

report->setReportRunner(  factoryReportRunner->getReportRunner(report->getDecorationType()  );

在 FactoryReportRunner 类中,您将有一个 getReportRunner(... )

getReportRunner(decorationType){
     $functionToCall = $this->'get'.decorationType.'ReportRunner';
     if(function_exists($functionToCall) && 
                      is_callable(array($this, $functionToCall))){
         return $this->$functionToCall();
     }else{
         //log or alert as error
     }
}

当然,那么每种装饰类型都必须有一个 getDecorationReportRunner,在本例中:getDigestableReportRunnergetSchedulableReportRunner 。 ReportProfile 的工厂也是如此。这样,每当您添加新的装饰类型时,您所要做的就是添加相应的 getDecorationReportRunner 以及与 ReportProfile 一起使用的装饰类型。

The only thing I can think of, if I understand your ordeal properly, is to use the Factory Design. It will be the Factory's responsibility to supply the ReportRunner and ReportProfile that corresponds to a particular Report based on its decoration.

So you'd have a class for each ReportRunner and one for ReportProfile, quite similar.

ReportRunner Factory should have a method that takes in the Report's decoration and returns it's ReportRunner so you'd do something like:

report->setReportRunner(  factoryReportRunner->getReportRunner(report->getDecorationType()  );

In the FactoryReportRunner class you'd have a method of getReportRunner(...)

getReportRunner(decorationType){
     $functionToCall = $this->'get'.decorationType.'ReportRunner';
     if(function_exists($functionToCall) && 
                      is_callable(array($this, $functionToCall))){
         return $this->$functionToCall();
     }else{
         //log or alert as error
     }
}

Of course, then you'd have to have a getDecorationReportRunner for each decoration type, in this case: getDigestableReportRunner and getSchedulableReportRunner. The same would go for the factory for ReportProfile. This way, any time you add a new decoration type, all you have to is add the corresponding getDecorationReportRunner and the one that goes with ReportProfile too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文