循环遍历子类还是有更好的方法吗?

时间:2012-07-30 11:09:06

标签: php architecture

我为一家保险经纪人工作,我在这里实施的其中一件事就是报价服务,它将客户的详细信息作为输入,并提供一些保险公司的报价清单作为回应。

目前的工作原理如下:

  • 索引页以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;
    }
}

3 个答案:

答案 0 :(得分:1)

尝试将此实现为单个PHP页面/脚本是非首发。一旦你开始分离逻辑,其他一切变得更加简单。

  

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

我敢打赌,演出也会耗费大量时间。

  

每组费率都是一个单一的代码块,因此无法单独测试各个部分

然后,至少应该是每个保险公司的一个URL,这可能反过来实现到该保险公司特定的其他URL的路由。当然,您将脚本实现为用于聚合Web服务的前端控制器。

  

似乎在寻找循环通过类的子类的最佳方法

之后

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

  

使用正确的子类(InsurerXMay2012 ....

天啊,不!

您感到困惑的是代码和数据。难怪each set of rates is a monolithic block of code

  

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

当您检查电子邮件时,您是否认为POP服务器会读取它所拥有的每封电子邮件,以便找到发给您的电子邮件?

虽然我认为最佳解决方案会将功能分散到多个聚合URL(即HTTP级架构),并且路由应该由数据构造(即数据库)驱动,但很有可能在合理的系统中实现所需的结果只使用面向对象的PHP。但是,除非您能够分离问题,分层代码架构,分离代码和数据,并研究如何正确地通过代码路由执行线程,然后你只是搞乱意大利面条代码。

答案 1 :(得分:0)

尽管可能,将变量数据存储在类名中并不是我建议的方法。 PHP语言中已存在用于此类数据存储的内置构造。

我不是为每种引用类型的每个日期都有一个子子类,而是使用类的构造函数来获取一个日期变量,用它来计算所需的引号。 / p>

例如:

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");

答案 2 :(得分:0)

我认为Insurer不应延长QuoteService。它们具有不同(如果偶尔重叠)的功能。 QuoteServiceInsuranceQuotes的聚合器。 InsurerInsuranceQuote的提供者。 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类。设计表来保存这些数据将是设计过程中最困难的部分。如果不了解您的数据形状,我不想建议一个结构。