我想知道是否有人可以给我一个建议,以便最好地处理这种情况:
我有几个系统可以从中提取数据以在单个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
答案 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[...] = ...;
}
}
}
new $type()
),但从不使用它!这是一个可怕的,如果过度使用make代码真的很糟糕。答案 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等)
当然,有很多方法可以将模型与数据映射驱动程序分开。重要的是,如果您使用的是这种不同的格式,您会找到最适合您的解决方案。