循环子类,或者有更好的方法


Loop through subclasses or is there a better way?

我在一家保险经纪人工作,我在这里实现的一件事是报价服务,它将客户的详细信息作为输入,并为许多保险公司提供报价列表作为响应。

它目前的工作方式如下:

  • 索引页接收JSON字符串中的报价数据
  • 报价数据依次发送到每个保险公司的计算脚本-这些脚本列在索引页面的数组中
    • 每个保险公司的计算都会参考一个数据库表,看看在保险开始的日期应该使用哪一组费率
    • 数据被发送到特定的脚本,并生成一个速率并返回到索引
  • 索引按升序对结果进行排序,并将其作为JSON字符串返回给发送方

这个解决方案的问题是:

  • 很难测试-每组速率都是一个单一的代码块,因此不能单独测试各个部分
  • 维护起来很尴尬——为保险公司添加一组新的费率需要将当前费率复制到一个新脚本中,进行必要的更改,并在数据库表中创建一个新条目,其中包含新费率的开始日期。这反过来又导致:
  • 费率之间存在大量代码重复

我新的部分形成的解决方案是有一个主Quoteservice类,它包含所有报价(无论保险公司或产品)通用的功能,以及从索引页面调用以获取报价率的Insurer1Insurer2等子类。反过来,每一组费率(Insurer1May2012Insurer1July2012等)都会有子类。我的问题是,试图摆脱对数据库表的需求,为开始日期选择正确的费率。

我想说

$quoteresults=array();
$quoteresults=array_merge(Insurer1->getQuote($quotedata), $quoteresults);
$quoteresults=array_merge(Insurer2->getQuote($quotedata), $quoteresults);

并让每个InsurerX对象根据开始日期使用正确的子类(InsurerXMay2012InsurerXJuly2012等)-可能是通过在其每个子类上调用函数getStartdate()来返回子类速率生效时的日期(或时间戳)。不幸的是,在寻找了遍历类的子类的最佳方法后,这似乎不是最好的方法

最终目标是能够仅为速率更改添加一个类型为Insurer1Sept2012的子类,而不必更改多个文件和/或数据库表。(覆盖以前的费率不是一种选择-调整过程需要能够在费率被取代后获得长达12个月的费率)

我如何看待新版本工作的示例

abstract class Quoteservice
{
    // Various common functionality here...
}
class Insurer1 extends Quoteservice
{
    public function getQuote($quotedata)
    {
        $subclass=null;
        // This is the bit I'm not sure of...
        // Maybe something like:
        foreach($subclasses as $thissubclass)
        {
            $subclassstart=$thissubclass->getStartdate();
            // Ignore all start dates greater than proposed start date
            if($subclassstart < $quotedata['startdate'])
            {
                $subclasses[$subclassstart]=$thissubclass;
            }
        }
        ksort($subclasses);
        $subclass=array_pop($subclasses);
        return $subclass->getQuote()
    }
}
class Insurer1May2012 extends Insurer1
{
    public function getStartdate()
    {
        return 1335830400; // unix time stamp for 1st May 2012
    }
    public function getQuote($quotedata)
    {
        // Calculate May's rate here...
        return $quoteinfo;
    }
}
class Insurer1July2012 extends Insurer1
{
    public function getStartdate()
    {
        return 1341100800; // unix time stamp for 1st July 2012
    }
    public function getQuote($quotedata)
    {
        // Calculate July's rate here...
        return $quoteinfo;
    }
}

尝试将其实现为单个PHP页面/脚本是不可能的。一旦你开始分离逻辑,其他的事情就会变得简单得多。

报价数据依次发送到每个保险公司的计算脚本

我打赌这场演出也很糟糕。

每组速率都是一个单一的代码块,因此不能单独测试

然后,作为最低要求,每个保险公司应该有一个URL,这反过来可能会实现到该保险公司特定的其他URL的路由。当然,您可以将脚本实现为聚合Web服务的前端控制器。

它似乎是在搜索了通过类的子类循环的最佳方式之后

您似乎错过了面向对象编程的一个关键点——封装。

使用正确的子类(InsurerXMay2012….

天啊,不!

您混淆了代码和数据。难怪each set of rates is a monolithic block of code

可能是通过在其每个子类上调用函数getStartdate()

当你检查你的电子邮件时,你认为POP服务器读取它保存的每一封电子邮件只是为了找到发给你的电子邮件吗?

虽然我认为最佳解决方案将跨多个聚合URL(即HTTP级别的架构)拆分功能,并且路由应该由数据结构(即数据库)驱动,但在一个合理的系统中仅使用面向对象的PHP就很可能实现所需的结果。但是,除非你能够分离这些问题,分层代码体系结构,分离代码和数据,并研究如何正确地通过代码路由执行线程,否则你只会把意大利面条代码弄得一团糟。

虽然可以,但我不建议使用将变量数据存储在类名中的方法。PHP语言中已经有了这种数据存储的内置结构。

我不会为每种报价类型的每个日期都有一个子类,而是使用该类的构造函数来获取一个日期变量,用它可以计算所需的报价。

例如:

class Insurer1 extends Quoteservice {
    public function __construct(DateTime $date) {
        // Do something with the data.
    }
}
// ... would be initialised like this:
$quoteResults = array();
$quoteResults[] = new Insurer1("2012-07-30");
$quoteResults[] = new Insurer2("2012-07-21");

Quoteservice类可以实现诸如getStartDate()之类的方法,这些方法将可用于Quoteservice的所有实例。

您甚至可以取消对Insurer1Insurer2。。。类,并将保险公司的名称作为另一个参数传递给构造函数。

$someQuote = new Quoteservice("2012-07-30", "Insurer1");

我认为Insurer不应该扩展QuoteService。它们具有不同的功能(如果偶尔重叠的话)。CCD_ 19是CCD_。CCD_ 21是CCD_。InsuranceQuote取决于Insurer是谁,Insuree可能是谁

$client= new Insuree;
$quoter= new QuoteService;
$quoter->calculate($client);
$quoter->show_quotes();

类如下:

class Insuree {
  protected $age;
  protected $gender;
  protected $marital_status;
  protected $drivers_license;
  protected $address;
  //etc. with constructor and getters/setters
}
class Insurer {
  function get_quote(Insuree $client, $args=null) {
    //do stuff
   return $quote; //InsuranceQuote obj.
  }
}
class InsuranceQuote {
  protected $period;
  protected $total;
  //etc. with constructor, getters, setters
  function out(){
    //echo something
  }
}
class QuoteService {
  protected $insurers; //SplObjectStorage containing Insurer objects
  protected $quotes; //SplObjectStorage containing InsuranceQuote objects
  function __construct($args=null){
     $this->get_insurers($args);  //limit the list with $args if you like
     $this->quotes=new SPLObjectStorage;
  }
  protected function get_insurers($args){
    $this->insurers=new SPLObjectStorage;
    //access list of insurers from db(?)
    while($row = $list->fetch_assoc()){
       $i=new Insurer($list);
       $this->insurers->attach($i);
    }
  }
  function calculate(Insuree $client, $args=null) {
    foreach($this->insurers as $quoter){
      $this->quotes->attach($quoter->get_quote($client, $args));
    }
  }
  function show quotes(){
    foreach($this->quotes as $quote) $quote->out();
  }
}

关于如何对Insurer进行子类化:每个Insurer特定的数据必须存储在某个地方。如果它可以以标准格式存储,那么您只需要一个Insurer类。设计保存这些数据的表将是设计过程中最困难的部分。在不了解更多数据形状的情况下,我不想提出一个结构。