Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 封装通用逻辑(领域驱动设计、最佳实践)_Oop_Domain Driven Design_Theory_Encapsulation - Fatal编程技术网

Oop 封装通用逻辑(领域驱动设计、最佳实践)

Oop 封装通用逻辑(领域驱动设计、最佳实践),oop,domain-driven-design,theory,encapsulation,Oop,Domain Driven Design,Theory,Encapsulation,更新:2009年2月9日-修订问题,提供更好的例子,增加悬赏 嗨, 我正在使用数据库和实体(域对象)之间的数据映射器模式构建一个PHP应用程序。我的问题是: 封装通常执行的任务的最佳方法是什么 例如,一项常见任务是从站点映射器检索一个或多个站点实体,并从页面映射器检索它们的关联(主页)页面实体。目前,我会这样做: $siteMapper = new Site_Mapper(); $site = $siteMapper->findByid(1); $pageMapper = new Pa

更新:2009年2月9日-修订问题,提供更好的例子,增加悬赏


嗨,
我正在使用数据库和实体(域对象)之间的数据映射器模式构建一个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());
$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();
function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}
这是一个相当简单的例子,但实际上它变得更复杂了,因为每个站点都有一个相关的区域设置,并且页面实际上有多个修订(尽管就本任务而言,我只对最近的一个版本感兴趣)

我需要在我的应用程序中的多个位置执行此操作(获取站点和相关主页、区域设置等),我想不出封装此任务的最佳方式/位置,因此我不必到处重复。理想情况下,我希望以这样的方式结束:

$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());
$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();
function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}
生成的站点实体已创建关联实体并准备好使用

将这些对象保存回时也会出现相同的问题。假设我有一个站点实体和关联的主页实体,它们都被修改了,我必须这样做:

$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());
$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();
function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}
同样,这个例子很简单,但是这个例子被简化了。代码重复仍然适用

在我看来,有某种中心对象可以照顾到:

  • 检索一个(或多个)站点和所有必要的关联实体
  • 创建具有新关联实体的新站点实体
  • 获取一个(或多个)站点并保存它和所有关联实体(如果它们已更改)
回到我的问题上来,这个物体应该是什么

  • 现有映射器对象
  • 基于存储库模式的东西*
  • 基于工作单位模式的东西*
  • 还有别的吗
*正如你可能猜到的那样,这两个我都不完全理解

有没有一个标准的方法来解决这个问题,有没有人能简单描述一下他们是如何实现的?我不想找任何人提供一个完整的工作实现,只是理论

谢谢,

Jack

我可能会先将公共任务提取到某个地方的助手方法,然后等待设计调用什么。感觉现在说还为时过早


这个方法叫什么名字?该名称通常提示该方法所属的位置。

[编辑:此条目试图说明这样一个事实,即编写自定义代码直接处理某个情况通常比尝试将问题放入模式更容易。]

模式在概念上很好,但它们并不总是“映射”。经过多年的高端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不会在请求之间保持“状态”,因此创建一个对象实例来立即销毁它通常是没有意义的。

使用存储库/服务模式,您的存储库类将为每个实体提供一个简单的CRUD接口,然后,服务类将是执行附加逻辑(如附加实体依赖项)的附加层。然后,应用程序的其余部分仅使用这些服务。您的示例可能如下所示:

$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());
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();
$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();
function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}
然后在SiteService类中,您将有如下内容:

$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());
$site = $siteService->getSiteById(1); // or
$sites = $siteService->getAllSites();
function getSiteById($id) {
  $site = $siteRepository->getSiteById($id);
  foreach ($pageRepository->getPagesBySiteId($site->id) as $page)
  {
    $site->pages[] = $page;
  }
  return $site;
}
我对PHP不太了解,所以如果语法有问题,请原谅。

将您的站点作为一个对象来封装复杂的关联并确保一致性

然后创建一个负责检索站点聚合并填充其子级(包括所有页面)的

您不需要单独的PageRepository(假设您不将页面设置为单独的聚合根),您的SiteRepository也应该负责检索页面对象(在您的情况下,使用现有的映射器)

因此:

然后
findById
方法将负责查找站点的所有页面子级。这将有一个类似于回答代码Monkey1的结构,但是我相信通过使用聚合和存储库模式,而不是为此任务创建特定的服务,您将受益更多。对站点聚合(包括其任何子对象)的任何其他检索/查询/更新都将通过同一SiteRepository完成


编辑:为了帮助您理解术语,尽管我建议您阅读整个图片。

hmm,非常好的问题:-),我认为我需要对此进行更多思考。虽然这是一个优雅的解决方案,但这对我的设置不起作用,根据数据映射器模式,我的实体不了解映射器,所以他们没有保存方法。实体被解析回映射器的save方法以保存到DB。这并不能真正解决使用(创建新实体、创建现有实体和保存)多个相关实体的问题。好的,这很有意义,需要了解服务模式。然而,(我已经遇到了这个问题),存储库和映射器之间的区别是什么。我的映射器已经提供了一个CRUD接口,那么为什么还要在上面增加存储库层呢?另外,你应该为每种类型的实体提供服务,还是只为那些你想定制的实体提供服务呢?我认为存储库与你的映射器非常相似,你可以保留它。新组件是服务层