这个问题在消除我的困惑方面做得很好,但是我很难找到关于服务层的确切限制应有的可靠信息来源。
对于此示例,假设我们正在处理书籍,并且我们希望按作者获取书籍。本BookDataMapper可以有一个通用的get()接受条件(S),如书的唯一标识符,作者姓名等这个实现是相当微不足道的(逻辑)方法,但如果我们希望有需要更复杂的查询多个条件是什么?
BookDataMapper
get()
假设我们要让某个作者在特定出版商下撰写的所有书籍。我们可以扩展该BookDataMapper->get()方法以解析多个条件,或者可以编写一个新方法,例如BookDataMapper->getByAuthorAndPublisher()。
BookDataMapper->get()
BookDataMapper->getByAuthorAndPublisher()
是让服务层直接调用这些[更特定的]方法,还是在BookDataMapper->get()通过多个条件调用更通用的方法之前对条件进行解析?在后一种情况下,服务层将执行更多的逻辑“繁重工作”,从而使数据映射器相当简单。前一种选择将服务层几乎全部缩减为一个中间人,而在诸如的方法中将条件逻辑留给了数据映射器BookDataMapper->getByAuthorAndPublisher()。
让服务层解析条件的显而易见的担忧是,某些域逻辑从数据映射器中泄漏出来。但是,如果服务层要处理条件,则逻辑不会使它超出模型层;$book_service->getByAuthorAndPublisher()无论如何,控制器都会调用。
$book_service->getByAuthorAndPublisher()
该 数据映射器 模式只告诉你,什么是应该做的,它不应该如何实现。 因此,本主题中的所有答案都应视为主观答案,因为它们反映了每个作者的个人喜好。
我 通常 尝试使mapper的界面尽可能简单:
fetch()
save()
remove()
我将条件保留在域对象本身中:
$user = new User; $user->setName( 'Jedediah' ); $mapper = new UserMapper; $mapper->fetch( $user ); if ( $user->getFlags() > 5 ) { $user->setStatus( User::STATUS_LOCKED ); } $mapper->save( $user );
这样,您可以具有多种检索条件,同时保持界面清洁。
不利的一面是,您需要一个公共方法来从域对象中检索信息以拥有这种fetch()方法,但是无论如何您都将需要它来执行save()。
没有真正的方法来实现映射器和域对象交互的 “告诉不要问” 的经验法则。
至于 “如何确保您确实需要保存域对象?” ,您可能会想到,此处已进行了介绍,其中包含大量的代码示例和注释中的一些有用内容。
如果您希望处理一组对象,那么应该处理不同的结构,而不是简单的 Domain Objects 。
$category = new Category; $category->setTitle( 'privacy' ); $list = new ArticleCollection; $list->setCondition( $category ); $list->setDateRange( mktime( 0, 0, 0, 12, 9, 2001) ); // it would make sense, if unset second value for range of dates // would default to NOW() in mapper $mapper = new ArticleCollectionMapper; $mapper->fetch( $list ); foreach ( $list as $article ) { $article->setFlag( Article::STATUS_REMOVED ); } $mapper->store( $list );
在这种情况下,集合是光荣的数组,具有接受不同参数的能力,然后将这些参数用作映射器的条件。当映射器尝试存储集合时,它还应允许映射器从该集合中获取已 更改的 域对象的列表。
在这种情况下,映射器应该能够构建(或使用预设的)条件下的所有 可能 条件的查询(作为开发人员,您将了解所有这些条件,因此您无需使其在无限条件下工作)为该集合包含的所有未保存域对象更新或创建新条目。
注意: 在某些方面,您可以说映射器与构建器/工厂模式有关。目标是不同的,但是解决问题的方法非常相似。