"Статические методы - смерть для тестируемости" - альтернативные конструкторы?

Говорят, что "статические методы - это смерть для тестируемости". Если это так, то каков жизнеспособный альтернативный паттерн для нижеприведенного?

class User {

    private $phone,
            $status = 'default',
            $created,
            $modified;

    public function __construct($phone) {
        $this->phone    = $phone;
        $this->created  = new DateTime;
        $this->modified = new DateTime;
    }

    public static function getByPhone(PDO $pdo, $phone) {
        $stmt = $pdo->prepare('SELECT * FROM `users` WHERE `phone` = :phone');
        $stmt->execute(compact('phone'));
        if (!$stmt->rowCount()) {
            return false;
        }

        $record         = $stmt->fetch(PDO::FETCH_ASSOC);
        $user           = new self($record['phone']);
        $user->status   = $record['status'];
        $user->created  = new DateTime($record['created']);
        $user->modified = new DateTime($record['modified']);
        return $user;
    }

    public function save(PDO $pdo) {
        $stmt = $pdo->prepare(
            'INSERT INTO `users` (`phone`, `status`, `created`, `modified`)
                  VALUES         (:phone,  :status,  :created,  :modified)
             ON DUPLICATE KEY UPDATE `status`   = :status,
                                     `modified` = :modified');

        $data = array(
            'phone'    => $this->phone,
            'status'   => $this->status,
            'created'  => $this->created->format('Y-m-d H:i:s'),
            'modified' => date('Y-m-d H:i:s')
        );

        return $stmt->execute($data);
    }

    ...

}

Это просто урезанный пример. У класса есть еще несколько методов и свойств, а также больше валидации при записи в базу данных и т.д. Ведущим принципом проектирования этого класса является то, что он моделирует пользователя как объект. Некоторые свойства объекта не могут быть изменены после его создания, например, номер телефона (который действует как первичный идентификатор), дата создания пользователя и так далее. Другие свойства могут быть изменены только в соответствии со строгими бизнес-правилами, которые все имеют строго проверяемые сеттеры и геттеры.

Объект не представляет собой запись базы данных как таковую, база данных рассматривается только как одна из возможных форм постоянного хранения. Таким образом, коннектор базы данных не хранится в объекте, а скорее должен вводиться каждый раз, когда объекту нужно взаимодействовать с базой данных.

Когда создается новый пользователь, это выглядит так:

$user = new User('+123456789');

Когда существующий пользователь восстанавливается из постоянного хранилища, это выглядит так:

$pdo  = new PDO('...');
$user = User::getByPhone($pdo, '+123456789');

Если серьезно отнестись к фразе "смерть тестируемости", то это якобы плохо. Однако я вполне могу протестировать этот объект, поскольку он полностью инжектирован зависимостями и статические методы не имеют состояния. Как я могу сделать это иначе и избежать использования статических методов? Или, скорее, что именно говорит против static в данном случае? Что делает это конкретное использование static методов таким трудным для тестирования?

11
задан deceze 23 January 2012 в 02:07
поделиться