如何避免模型臃肿与雄辩的模型?

flu*_*uke 2 php laravel

我一直在努力在闲暇时间在Laravel做一个小游戏,我一直遇到麻烦,如何最好地构建应用程序和某些模型的问题变得非常臃肿.

我目前正在使用雄辩的模型并直接将功能附加到它们.例如,我有一个User模型,它从以下函数开始

$user->verify()
$user->creditGold()
$user->debitGold()
Run Code Online (Sandbox Code Playgroud)

这看似合理.当我向网站添加功能时,该类开始变得越来越笨重,例如:

$user->creditItem()
$user->debitItem()
$user->equipItem()
$user->unequipItem()
$user->moveToTown()
$user->takeQuest()
$user->ban()
$user->unban()
// etc. etc. 
Run Code Online (Sandbox Code Playgroud)

有很多代码感觉非常无关,已被推入这一类并且它非常混乱.

我开始做的是制作由User类实例化并保存的辅助模型.以下示例

$user->inventory()->creditItem()
$user->inventory()->debitItem()
$user->inventory()->useItem()
Run Code Online (Sandbox Code Playgroud)

它很容易打电话和使用,但感觉不正确.

有没有人有关于如何最好地分解大量代码的建议,这些代码在概念上属于同一个实体?我喜欢将功能与数据结合起来的想法,因为我认为这是理解OO的最自然的方式,但是我最好将代码抽象到服务层并使用服务类将用户作为参数和代替它(即$service->giveItemToUser($user, $item))?

jar*_*dis 5

这就是SoC(关注点分离)原则变得非常重要的地方.这意味着确保您的应用程序的每个部分仅关注它需要关注的内容.

关注点分离

让我们首先确定您User班级中的一些问题.

  1. 库存
  2. 设备
  3. 任务

以上是您的用户将使用的一般资源.这些中的每一个都有他们关心的事情:

  1. 库存
    • 项目
  2. 设备
    • 项目
  3. 任务
    • 寻求

您已经可以看到我们有几个用户需要相同信息的独立部分.

将逻辑与国家分离

在这个阶段,我们现在需要将其他一些问题分开.具体来说,业务逻辑(我们想要对数据做什么)和数据访问层本身(ORM/Models).就个人而言,我喜欢使用存储库模式将这些事物分开.适用于模型并关注整体逻辑和应用程序过程的类.我觉得模型是状态的表示,应该只担心获取或持久化状态.

所以我把这些事情分开了:

  1. 楷模

    • 用户
    • 项目
    • 寻求
  2. 存储库(依赖项)

    • UserRepository(用户,物品,库存,设备,任务)
    • InventoryRepository(Item)
    • EquipmentRepository(Item,Collection)
    • QuestRepository(Quest)

代码示例

现在,这为我提供了我想要的设置和组织的明确定义.但是让我们举一些示例代码.这与数据如何持久化无关(手动或通过Eloquent关系等).

<?php namespace App\Repositories;

use App\Models\Item;
use Illuminate\Support\Collection;

class Inventory {

    protected $contents;

    public function __construct(Item $item, Collection $contents)
    {
        $this->item = $item;
        $this->contents = $contents;
    }

    public function add(Item $item)
    {
        $this->contents->push($item);
    }

    public function remove(Item $item)
    {
        $this->contents->forget($item->id);
    }

    public function contains(Item $item)
    {
        return $this->contents->has($item->id);
    }
}
Run Code Online (Sandbox Code Playgroud)

InventoryRepository仅关注管理其项目集合.添加它们,删除它们并检查是否存在其他项目.要做到这一点,它取决于Collection类和Item模型.

<?php namespace App\Repositories;

use App\Models\Item;

class Equipment {

    protected $slots = [
        'head' => null,
        'body' => null,
        'legs' => null,
        'feet' => null,
        'arms' => null,
    ];

    public function __construct(Item $item)
    {
        $this->item = $item;
    }

    public function get($slot)
    {
        return $this->slots[$slot];
    }

    public function set($slot, Item $item)
    {
        $this->slots[$slot] = $item;
    }

    public function empty($slot)
    {
        $this->slots[$slot] = null;
    }

    public function hasEquipment($slot)
    {
        return !empty($this->get($slot));
    }

    public function isEquipped(Item $item) 
    {
        if ($this->hasEquipment($item->slot))
        {
            return $this->get($item->slot)->id == $item->id;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

另一类只关注目前装备的物品.装备,装备等

将所有这些结合在一起

一旦定义了单独的部分,就可以将它们全部放入UserRepository类中.通过将它们作为依赖项引入,UserRepository中包含的代码将显式地基于用户管理,而访问加载的依赖项将为您提供所需的所有功能.

<?php App\Repositories;

use App\Models\User;
use App\Repositories\Inventory;
use App\Repositories\Equipment;

class User {

    protected $user;
    protected $quests;
    protected $equipment;
    protected $inventory;

    public function __construct(
        User $user,
        Quests $quests,
        Equipment $equipment,
        Inventory $inventory
    ) {
        $this->user = $user;
        $this->quests = $quests;
        $this->equipment = $equipment;
        $this->inventory = $inventory;
    }

    public function equip(Item $item)
    {
        if ($this->inventory->contains($item))
        {
            $this->equipment->set($item->slot, $item);
        }
    }

    public function unequip(Item $item)
    {
        if ($this->equipment->isEquipped($item)) 
        {
            $this->equipment->empty($item->slot);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这又是组织代码的概念.在这种类型的设置中,您希望如何将数据加载并保存到数据库.这也不是组织代码的唯一方法.这里要说的是如何将代码分解为单独的部分和关注点,以便更好地模块化和隔离功能,以便更容易管理和消化位.

我希望这很有帮助,不要犹豫,提出任何问题.