2

我们的网络应用程序允许用户上传文件。我们的 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. 我还能如何重构这些对象?

笔记

  • 我知道行数不是真正的衡量标准,但我们的其他课程是 50-150 行,很少使用。
  • 我已经减少了重要部分的常见用法,因此对缺乏最佳实践表示歉意。
  • 我已经在尝试从我们的代码中删除静态方法和公共变量。
4

1 回答 1

1

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

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,即 a 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;
    }
}

对于最后一个,我真的不建议构建自己的库(称为 ORM),存在许多出色的实现。看看Doctrine ORM

最后,我想说减少班级规模本身不应该是一个目标。但是,您可以通过提取对象本身不固有的逻辑来降低其复杂性,就像 owner 属性一样。另一个示例可能是文件的路径:可能路径是相对存储的,并且您具有将其转换为绝对路径或获取扩展名或文件名的功能。此功能可以在单独的FileMeta类中提取,或者您想要调用的任何内容,甚至可能是File不同命名空间中的类:FileSystem\File.

于 2013-11-06T16:17:07.270 回答