我们的网络应用程序允许用户上传文件。我们的Web应用程序中有两个上帝对象:
目前的常见用法是:
$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;
我们正在尝试重构这些神对象,并将用户对象添加到文件对象感觉就像我们使问题变得更糟。但是,如果不同意提议的更改,我真的没有一个好的论据。
在以下问题上获得一些帮助会很棒:
备注的
答案 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
。