Инкапсуляция общей логики (дизайн, управляемый доменом, лучшие практики) - PullRequest
2 голосов
/ 05 февраля 2009

Обновлено: 09.02.2009 - Пересмотрен вопрос, предоставлены лучшие примеры, добавлена ​​награда.


Привет
Я строю приложение PHP, используя шаблон отображения данных между базой данных и объектами (объектами домена). Мой вопрос:

Каков наилучший способ инкапсуляции часто выполняемой задачи?

Например, одной из распространенных задач является получение одного или нескольких объектов сайта из преобразователя сайтов и связанных с ними (домашних) объектов страницы из преобразователя страниц. В настоящее время я бы сделал это так:

$siteMapper = new Site_Mapper();
$site = $siteMapper->findByid(1);

$pageMapper = new Page_Mapper();
$site->addPage($pageMapper->findHome($site->getId()));

Теперь это довольно тривиальный пример, но в действительности он усложняется, поскольку каждый сайт также имеет связанный языковой стандарт, а страница фактически имеет несколько ревизий (хотя для целей этой задачи меня интересовали бы только самый последний).

Мне нужно будет сделать это (получить сайт и связанную домашнюю страницу, локаль и т. Д.) В нескольких местах в моем приложении, и я не могу придумать лучший способ / место для инкапсуляции этой задачи, чтобы я мог не нужно повторять это повсюду. В идеале я бы хотел получить что-то вроде этого:

$someObject = new SomeClass();
$site = $someObject->someMethod(1); // or
$sites = $someObject->someOtherMethod();

Там, где полученным объектам сайта уже созданы и готовы к использованию связанные объекты.

Та же проблема возникает при сохранении этих объектов обратно. Скажем, у меня есть объект сайта и связанный объект домашней страницы, и они оба были изменены, я должен сделать что-то вроде этого:

$siteMapper->save($site);
$pageMapper->save($site->getHomePage());

Опять тривиально, но этот пример упрощен. Дублирование кода все еще применяется.

На мой взгляд, имеет смысл иметь какой-то центральный объект, который мог бы позаботиться о:

  • Получение сайта (или сайтов) и всех связанных с ним объектов
  • Создание новых сущностей сайта с новыми связанными сущностями
  • Взятие сайта (или сайтов) и сохранение его и всех связанных объектов (если они изменились)

Итак, вернемся к моему вопросу, каким должен быть этот объект?

  • Существующий объект сопоставления?
  • Что-то основано на шаблоне хранилища? *
  • Что-то основано на единице работы патента? *
  • Что-то еще?

* Я не полностью не понимаю ни одного из них, как вы, вероятно, можете догадаться.

Существует ли стандартный способ решения этой проблемы, и может ли кто-нибудь дать краткое описание того, как они это реализовали? Я не ищу никого, кто бы предоставил полностью работающую реализацию, просто теорию.

Спасибо
Jack

Ответы [ 5 ]

2 голосов
/ 09 февраля 2009

Используя шаблон репозитория / сервиса, ваши классы репозитория предоставили бы простой интерфейс CRUD для каждой из ваших сущностей, тогда классы Службы были бы дополнительным уровнем, который выполняет дополнительную логику, такую ​​как присоединение зависимостей сущностей. Остальная часть вашего приложения использует только Сервисы. Ваш пример может выглядеть так:

$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();

Тогда внутри класса SiteService у вас будет что-то вроде этого:

function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}

Я не очень хорошо знаю PHP, поэтому извините, если что-то не так синтаксически.

1 голос
/ 09 февраля 2009

[Редактировать: эта запись пытается учесть тот факт, что зачастую проще написать собственный код для непосредственного решения ситуации, чем пытаться вписать проблему в шаблон.]

Шаблоны хороши в концепции, но они не всегда "отображаются". После нескольких лет разработки PHP высокого класса, мы остановились на очень прямом способе решения таких вопросов. Учтите это:

Файл: Site.php

class Site
{
   public static function Select($ID)
   {
      //Ensure current user has access to ID
      //Lookup and return data
   }

   public static function Insert($aData)
   {
      //Validate $aData
      //In the event of errors, raise a ValidationError($ErrorList)

      //Do whatever it is you are doing

      //Return new ID
   }

   public static function Update($ID, $aData)
   {
      //Validate $aData
      //In the event of errors, raise a ValidationError($ErrorList)

      //Update necessary fields
   }

Затем, чтобы вызвать его (откуда угодно), просто запустите:

$aData = Site::Select(123);

Site::Update(123, array('FirstName' => 'New First Name'));

$ID = Site::Insert(array(...))

Об OO-программировании и PHP нужно помнить одну вещь ... PHP не сохраняет «состояние» между запросами, поэтому создание экземпляра объекта для его немедленного уничтожения не часто имеет смысл.

0 голосов
/ 10 февраля 2009

Сделайте ваш объект объекта Совокупный корень для инкапсуляции сложной ассоциации и обеспечения согласованности.

Затем создайте SiteRepository , который отвечает за извлечение совокупности Сайта и заполнение его дочерних элементов (включая все страницы).

Вам не понадобится отдельный PageRepository (при условии, что вы не делаете страницу отдельным Агрегированным корнем), и ваш SiteRepository должен также нести ответственность за извлечение объектов страницы (в вашем случае с использованием существующих картографов).

Итак:

$siteRepository = new SiteRepository($myDbConfig);
$site = $siteRepository->findById(1); // will have Page children attached

И тогда метод findById будет также отвечать за поиск всех дочерних страниц Сайта. Это будет иметь структуру, аналогичную ответу, который дал CodeMonkey1, однако я полагаю, что вы получите большую выгоду от использования шаблонов Aggregate и Repository, а не от создания специальной службы для этой задачи. Любой другой поиск / запрос / обновление агрегата сайта, включая любые его дочерние объекты, будет осуществляться через тот же SiteRepository.

Редактировать: Вот краткое руководство DDD , чтобы помочь вам с терминологией, хотя я действительно рекомендую прочитать Эванс , если вам нужна вся картинка.

0 голосов
/ 09 февраля 2009
class Page {

  public $id, $title, $url;

  public function __construct($id=false) {
    $this->id = $id;
  }

  public function save() {
    // ...
  }

}

class Site {

  public $id = '';
  public $pages = array();

  function __construct($id) {
     $this->id = $id;
     foreach ($this->getPages() as $page_id) {
       $this->pages[] = new Page($page_id);
     }
  }

  private function getPages() {
    // ...
  }

  public function addPage($url) {
    $page = ($this->pages[] = new Page());
    $page->url = $url;
    return $page;
  }

  public function save() {
    foreach ($this->pages as $page) {
      $page->save();
    } 
    // ..
  }

}

$site = new Site($id);
$page = $site->addPage('/');
$page->title = 'Home';
$site->save();
0 голосов
/ 06 февраля 2009

Я бы, вероятно, начал с извлечения общей задачи где-нибудь во вспомогательный метод, а затем ждал бы, чтобы увидеть, что требует дизайн. Такое чувство, что пока рано говорить.

Как бы вы назвали этот метод? Название обычно намекает на то, где метод принадлежит.

...