Как правильно организовать классы?

Осваиваю возможности php, которых ранее не использовал, попутно переехав с php 5.2 на php 5.6, узнал о PSR-0,1,2,4 наткнувшись на этот (PHP Right Way) документ.

Вот ставлю задачу: реализовать возможность работы с различными типами хранилищ(git, mercurial).
делаю пакет в котором реализую:
  • Интерфейс: interface RepositoryInterface
  • Абстрактный класс: abstract class RepositoryAbstract
  • Класс для работы с хранилищем типа mercurial:
    class RepositoryHg extends RepositoryAbstract implements RepositoryInterface


Далее в коде сайта(или приложения, как будет угодно) я предполагаю, должно быть правильное использование интерфейса RepositoryInterface, на случай если, например, появится класс RepositoryGit, но я ни должен давать сайту(приложению) видеть RepositoryHg или RepositoryGit, если я правильно понял, для чего вообще ООП, ведь суть в том, чтобы ему было всё равно какой класс ему дают, главное чтобы он реализовывал нужный интерфейс.

Наверное в RepositoryAbstract должна быть реализован паттерн Фабрика и в приложении должен быть использован только интерфейс, но я не уверен, делаю всё впервые и прошу подсказку.
Спасибо.
  • Вопрос задан
  • 639 просмотров
Решения вопроса 2
Я использую такой подход:
Есть три интерфейса EntityInterface, RepositoryInterface, RepositoryDriverInterface
interface EntityInterface extends Serializable {
    public function getId();
}

interface RepositoryInterface {
    public function getDriver();
    public function findById($id, EntityInterface $Entity);
    public function save(EntityInterface $Entity);
}

interface RepositoryDriverInterface {
    public function set($id, $data);
    public function get($id);
}

Драйвер репозитория работает с базой данных. Сам репозиторий занимается сериализацией/десериализацией сущностей. Примеры драйверов:

Драйвер, работающий с массивами:
class RepositoryDriverArray implements RepositoryDriverInterface {
    private $data = [];

    public function set($id, $data) {
        $this->data[$id] = $data;
    }

    public function get($id) {
        return $this->data[$id];
    }
}

Драйвер работающий с Redis'ом:
class RepositoryDriverRedis implements RepositoryDriverInterface {
    protected $Redis = null;

    public function __construct(Client $Redis) {
        $this->Redis = $Redis;
    }

    public function set($id, $data) {
        $jsonData = json_encode($data);
        $this->Redis->hset($this->getContainerName(), $id, $jsonData);
    }

    public function get($id) {
        $jsonData = $this->Redis->hget($this->getContainerName(), $id);
        $data = json_decode($jsonData, true);

        return $data;
    }

    public function getContainerName() {
        return 'myContainer';
    }
}

Пример реализации самих репозиториев:
abstract class AbstractRepository implements RepositoryInterface {
    protected $RepositoryDriver = null;

    public function __construct(RepositoryDriverInterface $RepositoryDriver) {
        $this->RepositoryDriver = $RepositoryDriver;
    }

    public function save(EntityInterface $Entity) {
        $this->getDriver()->set($Entity->getId(), $Entity->serialize());
    }

    public function findById($id, EntityInterface $Entity) {
        $data = $this->getDriver()->get($id);
        $Entity->unserialize($data);

        return $Entity;
    }

    public function getDriver() {
        return $this->RepositoryDriver;
    }
}

Далее, пишем бизнес-логику:
class User implements EntityInterface {
    // TODO: Implement methods
}
class UserRepository extends AbstractRepository {}

И вот так работаем со всей этой системой:
$Redis = new Client([/* connection params */]);
$RedisDriver = new RepositoryDriverRedis($Redis);
$UserRepository = new UserRepository($RedisDriver);

$User = new User();
$UserRepository->save($User);

$User = new User();
$UserRepository->findById(1, $User);

Пишем юнит тесты? Подменяем драйвер репозитория:
$ArrayDriver = new RepositoryDriverArray();
$UserRepository = new UserRepository($ArrayDriver);

$User = new User();
$UserRepository->save($User);

$User = new User();
$UserRepository->findById(1, $User);


Такую систему очень просто тестировать. Всю "рутину" можно обернуть в различные фасады, "конфигураторы", провайдеры.
Я написал лишь примеры, конечно, в реальной системе больше возможностей драйвера, репозиториев.
Кэширование стоит делать на уровне репозитория. Драйвер занимается только сохранением/загрузкой данных.
Ответ написан
nazarpc
@nazarpc
Open Source enthusiast
Вы никогда не работаете "через интерфейс", хотя бы потому что он не имеет реализации.
Но вы можете сделать следующее:
...
public function setRepository (RepositoryInterface $repository) {
    $this->repository = $repository;
}
...


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

А вообще рекомендую почитать про Inversion of Control (IoC) и Dependency Injection (DI) - это вещи, которые вы захотите использовать с таким подходом.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы