如何构造几个PHP类

时间:2011-03-07 02:02:12

标签: php oop class

我想知道是否有人可以给我一个建议,以便最好地处理这种情况:

我有几个系统可以从中提取数据以在单个PHP驱动的网站上显示。跨系统(联系人,地址等)的信息类型将是相同的,但我提取数据的方式(MS-SQL,XML,REST)不会。

我想为每个连接类型创建一个类或一组类,并使用简单的方法,如getContact(),getAddress()等。我想知道如何最好地构建它。

最明显的想法是为每种连接类型创建类,例如:

class.sys_mysql.php. class.sys_xml.php, etc

但是我不会在每个班级重复这些方法吗?也许这没关系,但我很好奇,如果有更好的方法,就未来的维护而言。

也许我应该简单地将查询/数据提取方法隔离到单独的类文件中?课程中的课程?扩展课程?我对这些不太熟悉。

非常感谢任何建议。

DC

---------更多信息----------

大家好。我非常感谢所有伟大的建议。不要讨论这个问题,但我仍然对如何分解问题感到困惑。我会尝试更具体一点:

基本上,我有3个(将来会有更多)办公室,一个PHP网站从中提取信息。每个办公室使用不同的CRM,以及与该CRM连接的不同系统。一个使用MSSQL,另一个XML请求等。

每个办公室都希望在网站上同样显示信息,但存在细微差别。未来可能会有更多差异。但是,有更多的相似之处,所以我想利用更高级别的函数,比如它们之间共享的getContacts($ id)。

我正在尝试编写这些类,所以我可以:

1)使用更高级别的方法轻松提取数据

2)考虑不同的数据提取方式(xml,sql等)

3)说明数据在网站(办公室1,办公室2,办公室3)上的显示方式之间的差异

4)管理每个办公室的连接凭证并允许可扩展性_

5)我还应该提一下,我将创建单独的报告类,发送自动电子邮件,计算财务......需要使用现有类来提取数据的单独模块。

我意识到这里的一些例子看到了1和2,但我很困惑如何让3,4和5使用1和2。

我真的很感激帮助。

DC

4 个答案:

答案 0 :(得分:5)

这就是接口的用途。

定义与接口中的数据交互所需的方法,然后创建实现该接口的类

如果某些系统具有类似的访问模型(即可能是两个不同的DB服务器,但都使用PDO访问),您可以进一步抽象它并将“低级”功能放入特定于服务的类(实现接口) )然后是一个更高级的类,它定义了你使用的实际方法。

另一种选择是你可以将“常用”方法(那些相同或可以与服务类型检查相同的方法)放入基类中,而其他所有方法都扩展。

选项一的例子:

interface DataModel {
    public function findContacts($search);
    public function getContact($id);
    public function findAddresses($search);
    public function getAddress($id);
}

class XMLDataModel implements DataModel {
    public function findContacts($search) {
        ...
    }

    public function getContact($id) {
        ...
    }

    public function findAddresses($search) {
        ...
    }

    public function getAddress($id) {
        ...
    }
}

class RESTDataModel implements DataModel {
    public function findContacts($search) {
        ...
    }

    public function getContact($id) {
        ...
    }

    public function findAddresses($search) {
        ...
    }

    public function getAddress($id) {
        ...
    }
}

如您所见,您只需定义一个接口,它指定一个类必须实现的方法。

如果您有两个非常相似的类,可能一个用于MySQL,一个用于PostreSQL,并且您不能/不想将它们组合到一个PDO类中,您可以执行以下操作:

class PDODataModel implements DataModel {

    private $model;

    public function __construct ($serverType) {
        if ($serverType === 'mysql') {
            $this->model = new MySQLPDODataModel();
        }
        elseif ($serverType === 'postgresql') {
            $this->model = new PostgresQLPDODataModel();
        }
    }

    public function findContacts($search) {
        // common logic about $search, perhaps checking it's a valid search?

        $result = $this->model->searchForContacts($search);

        // more common logic, maybe higher level filtering..

        return $result;
    }

    public function getContact($id) {
        ...
    }

    public function findAddresses($search) {
        ...
    }

    public function getAddress($id) {
        ...
    }
}

interface PDODataModelDriver {
    public function searchForContacts($search);
}

class MySQLPDODataModel extends PDODataModel implements PDODataModelDriver {

    public function searchForContacts($search) {

        // MySQL-specific query to search for contacts
    }    

}

class PostgresSQLPDODataModel extends PDODataModel implements PDODataModelDriver {

    public function searchForContacts($search) {

        // PostgreSQL-specific query to search for contacts
    }    

}

我提到的另一个选择是朝着相反的方向工作:

abstract class PDODataModel implements DataModel {

    protected $pdo;
    protected $dsn;

    public function __construct () {
        $this->pdo = new PDO($this->dsn);
    }

    public function findContacts($search) {
        // common logic about $search, perhaps checking it's a valid search?

        $result = $this->searchForContacts($search);

        // more common logic, maybe higher level filtering..

        return $result;
    }

    public function getContact($id) {
        ...
    }

    public function findAddresses($search) {
        ...
    }

    public function getAddress($id) {
        ...
    }
}

class MySQLPDODataModel extends PDODataModel {

    protected $dsn = 'mysql:dbname=testdb;host=127.0.0.1';

    protected function searchForContacts($search) {

        // MySQL-specific query to search for contacts
    }    

}

class PostgresSQLPDODataModel extends PDODataModel {

    protected $dsn = 'pgsql:host=localhost;port=5432;dbname=testdb';

    protected function searchForContacts($search) {

        // PostgreSQL-specific query to search for contacts
    }    

}

答案 1 :(得分:2)

这是strategy design patter的经典示例。你的第一个想法是绝对正常的,但如果你在每个类中重复自己,你应该考虑创建一个处理公共代码的抽象类。

所以看起来像这样:

$myService = new MyService(new XMLReader('/path/to/file'));

echo $myService->getContanct('abc')->getName();

你班级的骨架:

 class MyService {
      private $reader;

      public function __construct(ReaderInterface $reader) {
          $this->reader = $reader;
      }

      // ...

      public function getContacnt($id) {
          $contact =  $this->reader->getContact($id);

          // do some extra stuff here

          return $contact;
      }
 }

 interface ReaderInterface {
      public function getContanct($id);
      public function getAddress($id);
 }

 abstract class AbstractReader implements ReaderInterface {
     protected $loaded = false;
     protected $data = array();

     abstract protected function load();

     public function getContanct($id) {
         if ($this->loaded == false) {
             $this->load();
             $this->loaded = true;
         }

         return $this->data['contact'][$id];
     }
 }

 class XMLReader extends AbstractReader {
      public function __construct($filepath) {
          ...
      }

      protected function load() {
          ...
          foreach (...) {
              $this->data[...] = ...;
          }
      }
 }

 class MSSQLReader extends AbstractReader {
      public function __construct(PDO $dbh) {
          ...
      }

      protected function load() {
          ...
          while ($row = $stmt->fetchRow()) {
              $this->data[...] = ...;
          }
      }
 }

编辑(2011-03-07) - 根据你的评论。

  1. PHP支持变量变量(new $type()),但从不使用它!这是一个可怕的,如果过度使用make代码真的很糟糕。
  2. 这是“经典问题”的又一个例子。使用factory pattern(根据创作的肤色,您可能希望使用更抽象的这种模式 - abstract factory
  3. 当您需要动态确定类名(例如,来自变量)时,使用reflection API来设置对象。

答案 2 :(得分:1)

您应该为每个数据源创建一个对象存储映射层,该层将对象实例化为存储不可知模型对象。见http://martinfowler.com/eaaCatalog/dataMapper.html

答案 3 :(得分:0)

如果您可以控制数据格式的结构,我建议您以一致的方式(尤其是XML格式)序列化数据,并为每种数据格式提供驱动程序。

例如,每个驱动程序都会有'findAll','getOne','count'等方法。可以为驱动程序提供一个模型来填充检索到的数据。

abstract class DataDriver {
    function __construct($model) {}
    abstract public function findAll();
    abstract public function getOne();
    abstract public function count();
    // ...
}
class XMLDriver extends DataDriver {
    // implements all the methods
}
class SQLDriver extends DataDriver {
    // implements all the methods
}

class Contact {
    public var $firstName;
    public var $lastName;

    function getFullName() {
        return trim($this->firstName . ' ' . $this->lastName);
    }
}

$accessor = new SQLDriver('Contact');
$contacts = $accessor->findAll();

如果您的数据将以不受控制的方式序列化,那么您建议的方法是最好的。只需确保将模型(例如地址簿,联系人)与检索方法分开(例如get_address_book_xml,get_address_book_sql等)

当然,有很多方法可以将模型与数据映射驱动程序分开。重要的是,如果您使用的是这种不同的格式,您会找到最适合您的解决方案。