【问题标题】:PHP: Keeping Objects Separate?PHP:保持对象分离?
【发布时间】:2011-07-07 01:06:54
【问题描述】:

我正在使用 PHP 处理一个复杂的项目,并且一直遇到同样的问题:如何将单独的对象分开?

OO 编程背后的理念是,没有一个对象需要知道任何其他对象在内部是如何工作的。但是对于第三个普通数据库,所有内容都在一个单独的表中,并且所有表都是相互关联的。

假设我有几个对象。一个 Order 对象(带有一个 Order 类和几个 Order 相关的数据库表)、带有一个 Address Class 和 Address 表的 Address 对象、Items(表和类)和 Customers(表和类)。

许多任务,例如运送订单,需要来自上述所有表的数据,并且所有数据都通过外键互连。那么,在不使代码成为灾难或违反 OO 原则的情况下,加载所有所需对象/数据的最优雅/最有效的方法是什么?

方法 A: 一个类执行单个查询并将其他类加载到自身中。

类订单{ 公共函数 LoadFromID($OrderID) { 从订单中选择 * 左加入客户 左连接地址 左连接项 $this->oCustomer = new Customer(); $this->oCustomer->SetName($W->CustomerName); $this->oAddress = 新地址(); $this->oAddress->SetCity($W->City); $this->oaItems = 数组(); foreach($w->Items AS $Item) { $oItem = 新项目(); $oItem->SetName($Item->ItemName); $this->oaItems[] = $oItem; } } }

问题在于 Order 类需要了解 Customers、Items 和 Addresses 表的布局方式。如果我们想走另一条路,让地址对象找到附加到它的订单和供应商怎么办?由于一切都是相互关联的,因此每个对象都需要知道每个其他对象的表是如何构造的。这意味着如果您想向表中添加新字段,则必须在每个对象中查找和编辑这些查询。这造成了巨大的维护问题。

方法 B: 一个类实例化自己并指示其他类在其内部实例化自己。

类订单{ 公共函数 LoadFromID($OrderID) { 从订单中选择 * $W = mysql_row(); // 一个客户对象被实例化并传递 // 要加载的客户 ID。 $this->oCustomer = new Customer($W->CustomerID); $this->oShipAddress = 新地址($W->ShipAddressID); $this->oBillAddress = 新地址($W->BillAddressID); $this->oaItems = 数组(); foreach($w->Items AS $Item) { $this->oaItems[] = new Item($Item->ItemID); } } } 类客户{ 公共函数 __construct($CustID) { SELECT * FROM Customers WHERE $CustID; $W = mysql_row(); $this->CustomerName = $W->Name; ... } }

这种方法将对象分开,只有一个类可以访问自己的表,但引入了新问题。首先,从 MySQL 的角度来看,它是低效的,执行许多查询可以获得所有需要的数据。不确定性能是否可以忽略不计。此外,一个对象永远不知道它是由控制器还是由另一个对象创建的,因为对象可以在任何地方生成。当您需要使用围绕多个对象执行多项操作的事务时,这尤其成问题。 “开始”和“提交”的呼唤去哪儿了?作为一个对象,我是否已经在一个事务中,还是我需要自己开始一个事务?当方法用于多个特定任务时,这种不一致可能会发生冲突。有时一个方法需要创建事务,有时它已经在其中。

方法 C: 控制器实例化所有对象。

类控制器{ 公共函数 DoSomething() { $DB->开始(); $oOrder=新订单(); $aAddressIDs = $oOrder->GetAddressIDs(); $CustomerID = $oOrder->GetCustomerID(); foreach($aAddressIDs AS $ID) { // 获取订单的地址 ID // 然后把它们变成地址对象 // 将它们传递回订单。 $oAddress = 新地址($ID); $oOrder->AddAddress($oAddress); } // 为订单创建一个客户 ID // 并将构建的对象传回订单中。 $oCustomer = 新客户($CustomerID); $oOrder->AddCustomer($oCustomer); if($oOrder->DoSomethingComplex()) $DB->提交(); 别的 $DB->回滚(); } }

这通过将它们放在对象之外来解决有关启动和完成数据库事务的一致性问题。此外,对象的实例化位置也不会混淆,因为所有对象都必须由控制器创建。但这看起来真的很草率,并且要求每个控制器方法都知道 Order 对象需要什么才能运行。

有什么干净的方法可以做到这一点吗?

【问题讨论】:

    标签: php mysql


    【解决方案1】:

    抱歉,您掉入了一个众所周知的陷阱。
    对象不应镜像数据库。 订单对象需要获取所有相关数据才能将订单处理成自己。
    他绝对可以使用其他类/对象(用于获取正确排序的订单项的树对象、数据库连接类、Sql 抽象类等)
    如果可以,您应该在一个查询中加载数据。每张表没有一个查询。
    如果您想采用每个数据源一个查询的范例,我会说关系数据库不是您想要的,而是 NoSQL DB(例如 MongoDB),但是有同样,您只需要使用一个对象来处理订单,因为 MongoDB 几乎强制执行此操作。

    【讨论】:

    • 但是如果地址对象需要某种处理才能正常工作呢?订单对象(以及使用地址对象的任何其他对象)需要了解这一点,并且要么处理地址本身,要么调用特定方法,这两者都需要外部对象了解地址对象的内部工作。
    • @Nick - 就是这样,在这里你确定了一个在你的其他几个进程中使用的单一进程(它应该从多个地方读取数据,可能) -> 开始并封装整个对象中的逻辑,并将其移动到需要读取/解析/保存/搜索的地址的人周围,订单对象在内部使用地址对象没有任何问题。 Address 对象由 Order 对象实例化,该对象应具有在一般查询中获取的原始地址数据。
    猜你喜欢
    • 2010-10-10
    • 1970-01-01
    • 2018-01-25
    • 2012-05-14
    • 2014-10-13
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 1970-01-01
    相关资源
    最近更新 更多