Я работаю над сложным проектом на PHP и продолжаю сталкиваться с одной и той же проблемой: как разделить отдельные объекты?
Идея ОО-программирования заключается в том, что ни одному из объектов не нужно знать, какдругой объект работает внутри.Но с третьей нормальной базой данных все находится в отдельной таблице, и все таблицы взаимосвязаны.
Допустим, у меня есть несколько объектов.Объект Order (с классом Order и несколькими таблицами базы данных, относящимися к Order), Объекты Address с классом Address и таблицами Address), Items (таблицы и класс) и Customers (таблица и класс).
Многие задачиНапример, при отправке заказа требуются данные из всех вышеперечисленных таблиц, и все данные связаны между собой посредством внешних ключей.Итак, каков самый элегантный / эффективный способ загрузки всех необходимых объектов / данных, без превращения кода в катастрофу или нарушения принципов ОО?
Подход 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, он выполняет много запросов, где можно получить все необходимые данные.Не уверен, что производительность незначительна или нет.Кроме того, объект никогда не знает, был ли он создан контроллером или другим объектом, поскольку объекты могут быть созданы где угодно.Это особенно проблематично, когда вам нужно использовать транзакции, которые оборачиваются вокруг нескольких объектов, делая несколько вещей.Куда идет вызов «begin» и «commit»?Как объект, я уже нахожусь в транзакции, или мне нужно начать ее самостоятельно?Это несоответствие может конфликтовать, так как метод используется для более чем одной конкретной задачи.Иногда метод должен создать транзакцию, а иногда он уже внутри нее.
Подход 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 для функционирования.
Есть ли какой-нибудь чистый способ сделать это?