如何避免对我们的上帝对象的这种添加

时间:2013-11-06 15:41:59

标签: php oop refactoring god-object

我们的网络应用程序允许用户上传文件。我们的Web应用程序中有两个上帝对象:

  • 用户对象(3000多行)
  • 文件对象(3000+行)

目前的常见用法是:

$file = File::getByID($_GET['file_id']);
$user = User::getByID($file->ownerID);

echo $file->title;
echo $user->name;

我们目前正在讨论将用户对象添加到文件对象作为另一个公共变量。因为我们在任何地方都使用文件对象,所以我们需要关联的用户对象。

因此用法将是:

$file = File::getByID($_GET['file_id']);

echo $file->title;
echo $file->user->name;

我们正在尝试重构这些神对象,并将用户对象添加到文件对象感觉就像我们使问题变得更糟。但是,如果不同意提议的更改,我真的没有一个好的论据。

在以下问题上获得一些帮助会很棒:

  1. 将用户对象添加到文件对象是个坏主意吗?
  2. 如果这是一个坏主意,不这样做的主要理由是什么?
  3. 如何重构这些对象?
  4. 备注

    • 我知道行数并不是真正的衡量标准,但我们的其他类别是50-150行并且很少使用。
    • 我已经减少了重要位的常用用法,因此缺乏最佳实践道歉。
    • 我已经尝试从代码中删除静态方法和公共变量。

1 个答案:

答案 0 :(得分:1)

我肯定会将User对象添加为File类的属性,因为这是它背后的数据模型的实际表示。 它还将使代码更加清晰,并将生成File对象的更简洁的使用代码。 这也意味着如果设置了所有者ID,File类不必进行任何有效性检查:如果传递了一个对象,它可以认为它是有效的(或者它不应该被构造)。

class File
{
    protected $owner;

    /**
     * @var User $owner mandatory
     */
    public function __construct(User $owner)
    {
        $this->setOwner($owner);
    }

    public function setOwner(User $owner)
    {
        return $this->owner;
    }

    public function getOwner()
    {
        return $this->owner;
    }
}

现在很明显,File有一个强制属性owner,即User

我还建议使用访问器而不是公共属性。这将是更具前瞻性的证明,例如,如果您愿意,您也可以延迟加载用户。

class File
{
    protected $ownerId;
    protected $owner;

    public function getOwner()
    {
        if (!$this->owner && $this->ownerId) {
            $this->owner = User::getById($this->ownerId);
        }
        return $this->owner;
    }
}

这意味着你的数据层相应地填充了对象,并且还需要一个适应的构造函数,丢失自动类型检查(只有构造函数,仍然在setter中有类型检查):

/**
 * @var $owner int|User the user object or it's ID
 */
public function __construct($owner)
{
    if (is_object($owner)) $this->setOwner($owner);
    else $this->ownerId = $owner;
}

关于静态User::getById()应该去哪里的问题:你应该在单独的层中分离数据操作(对象的检索和持久化)。

class UserStore
{
    public function getById($id)
    {
        // code to retrieve data from where ever, probably database
        // or maybe check if the user was already instantiated and registered somewhere...
        $user = new User($data['name'], $data['email']/*, ....*/);
        return $user;
    }
}

对于最后一个,我真的不建议建立自己的库(called an ORM),存在许多很棒的实现。看看Doctrine ORM

最后我要说减少课程的大小本身并不是一个目标。但是,您可以通过提取对象本身不固有的逻辑来降低其复杂性,就像拥有者属性的情况一样。 另一个例子可能是文件的路径:也许路径是相对存储的,你有功能将其转换为绝对路径,或获取扩展名或文件名。此功能可以在单独的FileMeta类中提取,也可以在您要调用的任何内容中提取,甚至可以在不同的命名空间中提取File类:FileSystem\File