Ответы пользователя по тегу Паттерны проектирования
  • Как правильно организовать классы?

    Я использую такой подход:
    Есть три интерфейса 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);


    Такую систему очень просто тестировать. Всю "рутину" можно обернуть в различные фасады, "конфигураторы", провайдеры.
    Я написал лишь примеры, конечно, в реальной системе больше возможностей драйвера, репозиториев.
    Кэширование стоит делать на уровне репозитория. Драйвер занимается только сохранением/загрузкой данных.
    Ответ написан
    1 комментарий