2

我正在将 ZF 用于 MVC 应用程序,并且对我的代码应该如何构造感到非常困惑。

我有一个程序应用程序,它基本上是 1 个巨大的长文件,其中包含我希望我的应用程序执行的所有功能......比如:getUsername($id) 等。所以现在我在 ZF 中重新制作整个东西,因为我的当前的代码库不可行,垃圾且难以调试。

我是 MVC 的新手,对它应该如何布局、应该与什么对话等感到非常困惑。所以我知道关于模板的视图和需要瘦的控制器,并且你应该有胖模型,但我是混淆了逻辑需要在哪里。

我正在制作一个游戏,里面有一些常见的对象,比如......用户、村庄、军队、地图广场、资源等。

如果我完全从理论上考虑它,我只会说:1 个用户对象包含许多村庄,每个村庄属于一个方格并包含一支军队(其中包含许多单位)。

所以我认为我的模型不应该包含任何逻辑,只是一个用于检索数据的 get 和 set 函数列表,并且用于处理的逻辑、提问应该在 Mapper 内部完成......比如:

$villageMapper = new VillageMapper();
// Get village from database using mapper 
$village = $villageMapper->getVillage($id, new Village());

当我想确定说两个村庄互相攻击的结果时,会在哪里做呢?我会做类似的事情:

$outcome = $villageMapper->determineAttackOutcome($village1, $village2);

或者我会说……一个里面有一点逻辑的战斗对象?

$battle = new Battle();
// Add participants
$battle->addAttacker($village1)->addDefender($village2);
$outcome = $battle->performAttack();
// Save village changes cause of battle
$villageMapper->save($battle->getAttacker());
$villageMapper->save($battle->getDefender());

我有一堆 DbTable php 文件,我猜所有的数据库代码都在里面......所以我的问题是:我的 Mapper 对象是否应该只用于诸如获取和保存到数据库之类的事情?

谢谢, 多姆

4

3 回答 3

5

MVC有很多不同的解释,但我是这样理解的:

模型:几乎包含与特定项目有关的所有逻辑。必须建模的每件事(在您的情况下是用户、村庄等)都有一个与之配套的模型。该模型具有取出数据和放入数据的功能(即getter 和setter)。该模型还进行错误检查等,并确保没有输入任何冲突。

观点:没有任何逻辑。在 Web 应用程序中,这实际上只是说明在页面上放置内容的位置。在某些框架中,您可以为视图提供模型(即 ASP.NET MVC3),而在其他框架(如 Savant3 for php)中,它可以提供任何内容。控制器通常为视图提供数据,但如果视图被赋予模型,它只是从模型中读取而不写入。

控制器:控制用户和模型之间的交互。当用户做某事时,控制器将其转换为模型必须做的事情。例如,如果你对程序说“请将我的角色向北移动 6 格”,控制器会说“有什么东西可以跑到这里以北 6 格吗?” 如果它看到该位置很清晰,它会告诉角色模型“向北移动 6 个空格”。完成此操作后,它将向视图发送有关因此应显示的任何内容的数据。控制器中实现的大多数实际逻辑应该是用户模型而不是模型模型。模型之间的交互可以由单个模型中的方法或封装某种行为或交互的其他模型来处理。

因此,关于您的实施:

我会制作一个战斗对象(这是一个模型),它的构造函数需要两个村庄或任何正在战斗的东西。它将有一个名为 execute 或 doBattle 的方法或控制器调用的方法,然后战斗对象将执行其逻辑来决定结果并更新战斗人员的状态(即降低 hp、给予经验等)。它会将结果返回给控制器,以便控制器知道要做什么(即,如果控制器因为模型死亡而需要忘记模型,它会告诉它)。这个返回值也可以是发送到视图以告知战斗结果的东西。

您的一些模型(例如用户、村庄等)将保存在数据库中,因此模型将知道如何将自己映射到该数据库(或者它会与知道如何映射它的另一层对话)并且还采用关心更新数据库和东西的确切实现(控制器将调用实际方法来“保存”,但模型将是唯一知道幕后发生了什么的东西)。其他模型(例如战斗)不需要存在于数据库中,因为它们只是封装了一些交互的逻辑。

于 2012-04-12T20:37:05.470 回答
1

拥有一个胖模型意味着几乎所有的逻辑都存在于模型中。

一些建议...

如果您正在进行领域驱动设计 (http://en.wikipedia.org/wiki/Domain-driven_design),您的村庄对象可能是管理该村庄业务逻辑的聚合根。

一场战斗也可以是由两个(或更多)村庄对象组成的聚合根,或者是接受两个村庄对象并返回“结果”对象的服务。你也可以按照 $village->attack($anotherVillage) 的方式做一些事情,它可以返回一个你可能会坚持的战斗对象。

在创建和持久化这些业务对象时,我建议遵循域驱动设计和存储库模式http://msdn.microsoft.com/en-us/library/ff649690.aspx

于 2012-04-12T20:28:57.560 回答
1

Datamapper 只能用于从数据库中存储和检索数据,并将该数据映射到您的域对象(用户、村庄、军队、地图方阵)。

您可以将逻辑放在域对象中,但我喜欢使用服务层。

在您的控制器中,您将执行以下操作:

function  attackAction() {
    $gameService->doVillageBattle($villageId1,$villageId2);
}

GameService 看起来像:

doVillageBattle($villageId1,$villageId2) {
    $village1 = villageService->getById( $villageId1);
    $village2 = villageService->getById( $villageId2);

    if ($village1->getStrength() > $village2->getStrength()) {
         $village1->winBattle();
         $village2->looseBattle();
         $villageService->save($village1);
         $villageService->save($village2);
    }

}

最后 VillageService 将拥有:

function save( $village ) {
    villageMapper->save( $village );
}

因此控制器只与服务对话,服务彼此对话或与逻辑关联的数据映射器对话。服务承载大部分“业务逻辑”并且独立于数据库。数据映射器当然独立于服务和控制器。

于 2012-04-12T20:49:09.560 回答