我正在使用PHP开发一个复杂的项目并继续遇到同样的问题:如何将单独的对象分开?
OO编程背后的想法是,没有任何对象需要知道任何其他对象如何在内部工作。但是对于第三个普通数据库,所有内容都在一个单独的表中,并且所有表都是互连的。
假设我有几个对象。 Order对象(带有Order类和几个Order相关的数据库表),Address对象带有Address Class和Address表),Items(表和类)和Customers(表和类)。
许多任务(例如发送订单)需要来自所有上述表格的数据,并且所有数据都通过外键进行互连。那么,在不使代码成为灾难或违反OO主体的情况下,加载所需的所有对象/数据的最优雅/最有效的方法是什么?
接近A : 一个类执行单个查询并将其他类加载到自身中。
class Order { public function LoadFromID($OrderID) { SELECT * FROM Orders LEFT JOIN Customers LEFT JOIN Addresses LEFT JOIN Items $this->oCustomer = new Customer(); $this->oCustomer->SetName($W->CustomerName); $this->oAddress = new Address(); $this->oAddress->SetCity($W->City); $this->oaItems = array(); foreach($w->Items AS $Item) { $oItem = new Item(); $oItem->SetName($Item->ItemName); $this->oaItems[] = $oItem; } } }
问题是Order类需要了解Customers,Items和Addresses表的布局方式。如果我们想要转向其他方式并让地址对象找到附加到它的订单和供应商,该怎么办?由于一切都是相互关联的,因此每个对象都需要知道每个其他对象的表是如何构造的。这意味着如果要向表中添加新字段,则必须在每个对象内查找和编辑这些查询。这造成了巨大的维护问题。
方法B : 一个类实例化自身并指示其他类在其自身内实例化。
class Order { public function LoadFromID($OrderID) { SELECT * FROM Orders $W = mysql_row(); // A customer object is instantated and passed // a customer ID to load from. $this->oCustomer = new Customer($W->CustomerID); $this->oShipAddress = new Address($W->ShipAddressID); $this->oBillAddress = new Address($W->BillAddressID); $this->oaItems = array(); foreach($w->Items AS $Item) { $this->oaItems[] = new Item($Item->ItemID); } } } class Customer{ public function __construct($CustID) { SELECT * FROM Customers WHERE $CustID; $W = mysql_row(); $this->CustomerName = $W->Name; ... } }
此方法将对象分开,只有一个类可以访问自己的表,但会引入新问题。首先,从MySQL的角度来看,它是低效的,可以进行许多查询,其中可以获得所有需要的数据。不确定性能是否可以忽略不计。此外,对象永远不知道它是由控制器还是其他对象创建的,因为对象可以在任何地方生成。当您需要使用包含多个对象的事务处理多个事务时,这尤其成问题。 “开始”和“提交”的调用在哪里?作为一个对象,我已经在交易中,还是我需要自己创建一个?这种不一致可能会发生冲突,因为方法用于多个特定任务。有时一个方法需要创建事务,有时它已经在它内部。
方法C : 控制器实例化所有对象。
class Controller { public function DoSomething() { $DB->Begin(); $oOrder=new Order(); $aAddressIDs = $oOrder->GetAddressIDs(); $CustomerID = $oOrder->GetCustomerID(); foreach($aAddressIDs AS $ID) { // Take the order's address IDs // and turn them into adress objects then // pass them back to the order. $oAddress = new Address($ID); $oOrder->AddAddress($oAddress); } // Create a customer ID for the order // and pass the built object back into the order. $oCustomer = new Customer($CustomerID); $oOrder->AddCustomer($oCustomer); if($oOrder->DoSomethingComplex()) $DB->Commit(); else $DB->Rollback(); } }
通过将数据库事务放在对象之外,解决了启动和完成数据库事务的一致性问题。此外,对于实例化对象的位置没有混淆,因为所有对象必须由控制器创建。但这看起来很草率,需要每个控制器方法知道Order对象需要什么才能运行。
有没有干净的方法可以做到这一点?
答案 0 :(得分:2)
对不起,你已经陷入了一个众所周知的陷阱
对象不应该镜像数据库。
订单对象需要获取所有相关数据才能将订单处理为自己
他绝对可以使用其他类/对象(一个树对象来获取正确排序的订单项,一个db连接类,一个Sql抽象类等)
如果可以,您应该在一个查询中加载数据。每个表不是一个查询
如果你想使用每个数据源一个查询的范例,我会说关系数据库不是你想要的,而是一个NoSQL数据库(比如像MongoDB),但是那里同样,你需要使用一个对象来处理订单,因为MongoDB几乎强制执行此操作。