Как избежать этого дополнения к нашему божественному объекту
Наше веб-приложение позволяет пользователям загружать файлы. В нашем веб-приложении есть два объекта бога:
- Пользовательский объект (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;
Мы пытаемся реорганизовать эти объекты бога, и добавление пользовательского объекта к объекту файла кажется, что мы усугубляем проблему. Однако у меня нет веских аргументов, чтобы не согласиться с предлагаемым изменением.
Было бы здорово получить некоторую помощь по следующим вопросам:
- Является ли добавление пользовательского объекта в файловый объект плохой идеей?
- Если это плохая идея, каков главный аргумент, чтобы не делать этого?
- Как еще я могу провести рефакторинг этих объектов?
Заметки
- Я знаю, что количество строк не является верным показателем, но другие наши классы имеют 50-150 строк и используются редко.
- Я сократил общее использование до важных битов, поэтому извиняюсь за отсутствие лучших практик.
- Я уже пытаюсь удалить статические методы и публичные переменные из нашего кода.
1 ответ
Я бы определенно добавил User
объект как свойство File
класс, так как это фактическое представление модели данных за ним. Это также придаст коду большую ясность и даст более краткий код использования File
объект. Это также будет означать 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;
}
}
Это подразумевает, что ваш слой данных соответствующим образом заполняет объект, а также потребует адаптированного конструктора с потерей автоматической проверки типа (только конструктор все еще имеет проверку типа в установщике):
/**
* @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
,