Я пытаюсь сделать библиотеку кэша с открытым исходным кодом. Цель библиотеки — предоставить способ хранения переменной (может быть объектом, может быть массивом, может быть чем угодно) в файлах, а затем извлекать ее обратно по вызову. (обычно значение этих переменных является результатом массивных запросов к базе данных и вычислений).
Основная цель проекта заключается в применении принципа объектно-ориентированного проектирования, называемого Solid.
Если кто-то может указать, где я нарушаю твердый принцип и как это исправить
Я полностью понимаю, что stackoverflow не является сервисом для написания кода, но я делаю эту библиотеку с открытым исходным кодом, так что это пойдет на пользу нашему сообществу.
Итак, вот моя файловая структура.
Я новичок в UML, поэтому, пожалуйста, игнорируйте, если обнаружены какие-либо ошибки
Вот реализация классов.
кэш
namespace library\pingle\cache;
use library\pingle\cache\config\CacheConfigurator;
use library\pingle\cache\file\FileHandler;
/**
* @property CacheReader $cache_reader
* @property CacheWriter $cache_write
*/
Class Cache {
private $config;
private $file_hander;
private $cache_reader;
private $cache_write;
private $cache_directory;
private $cache_kept_days;
private $cache_file_prams;
private $function_name;
private $file_path;
function __construct(CacheConfigurator $config) {
$this->file_hander = new FileHandler();
$this->config = $config;
list($this->cache_directory, $this->function_name, $this->cache_kept_days, $this->cache_file_prams) = $this->config->getConfig();
$this->file_path = $this->generateFileName($this->cache_file_prams);
}
public function read() {
if (is_null($this->cache_reader)) {
$this->cache_reader = new CacheReader($this->file_hander);
}
return $this->cache_reader->readCache($this->file_path);
}
public function write($data) {
if (is_null($this->cache_write)) {
$this->cache_write = new CacheWriter($this->file_hander);
}
if (!$this->file_hander->checkDirectory($this->cache_directory . "/" . $this->function_name)) {
$this->file_hander->createDirectory($this->cache_directory . "/" . $this->function_name);
}
$this->cache_write->writeCache($this->file_path, $data);
}
public function check() {
if ($this->file_hander->checkFileExits($this->file_path)) {
if (time() - filemtime($this->file_path) >= 60 * 60 * 24 * $this->cache_kept_days) {
return false;
}
return true;
} else {
return false;
}
}
private function generateFileName(Array $nameprams) {
$this->file_name = "";
$file = "CC";
foreach ($nameprams as $key => $value) {
$file .= "-$key|$value-";
}
$file .= ".bak";
return $this->cache_directory . "/" . $this->function_name . "/" . $file;
}
}
AbstractCache
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
abstract Class AbstractCache {
protected $file_handler;
public function __construct(FileHandler $file_handler) {
$this->file_handler = $file_handler;
}
protected function checkDirectory($path) {
//check directory exists
$dircheck = $this->file_handler->checkDirectory(dirname($path));
if ($dircheck) {
//check directory permission
if ($this->file_handler->checkPermission(dirname($path))) {
return true;
} else {
throw new \Exception("Directory ($path) Permission Error.");
}
} else {
throw new \Exception("Directory ($path) not found.");
}
}
}
CacheReader
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
/**
* @property FileHandler $file_handler
*/
Class CacheReader extends AbstractCache {
public function __construct(FileHandler $file_handler) {
parent::__construct($file_handler);
}
public function readCache($path) {
if ($this->checkDirectory($path)) {
//delete the file if it exits
if ($this->file_handler->checkFileExits($path)) {
return $this->file_handler->readFile($path);
} else {
throw new \Exception("File ($path) not found");
}
}
}
}
CacheWriter
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
/**
* @property FileHandler $file_handler
*/
Class CacheWriter extends AbstractCache {
public function __construct(FileHandler $file_handler) {
parent::__construct($file_handler);
}
function writeCache($path, $data) {
if ($this->checkDirectory($path)) {
//delete the file if it exits
if ($this->file_handler->checkFileExits($path)) {
$this->file_handler->deleteFile($path);
}
//write cache
$this->file_handler->writeFile($path, $data);
}
}
}
FileHandler
<?php
namespace library\pingle\cache\file;
Class FileHandler {
public function writeFile($path, $data) {
$content = serialize($data);
file_put_contents($path, $content);
}
public function createDirectory($path) {
mkdir($path);
}
public function deleteFile($path) {
unlink($path);
}
public function checkDirectory($path) {
if (file_exists($path)) {
return true;
} else {
return false;
}
}
public function checkPermission($path) {
if (is_writable($path)) {
return true;
} else {
return false;
}
}
public function checkFileExits($path) {
if (is_file($path)) {
return true;
}
return false;
}
public function readFile($path) {
return unserialize(file_get_contents($path));
}
public function checkFileCreated($path, $format = "Y-m-d") {
return date($format, filemtime($path));
}
}
CacheConfigurator
<?php
namespace library\pingle\cache\config;
/**
* @property PramsFormatter $prams_formatter
*/
class CacheConfigurator {
private $prams_formatter;
private $cache_directory;
private $cache_kept_days;
private $cache_file_prams;
private $function_name;
function __construct($file_prams) {
$this->cache_file_prams = $file_prams;
$this->cache_directory = ""; //Def Path
}
public function setCacheDirectory($cache_directory) {
$this->cache_directory = $cache_directory;
return $this;
}
public function setFunction($function) {
$this->function_name = $function;
return $this;
}
public function setCacheKeptDays($cache_kept_days) {
$this->cache_kept_days = $cache_kept_days;
return $this;
}
public function getConfig() {
$this->prams_formatter = new PramsFormatter($this->cache_file_prams);
$this->cache_file_prams = $this->prams_formatter->getFormattedPrams();
$this->function_name = $this->prams_formatter->cleanValue($this->function_name);
return array($this->cache_directory, $this->function_name, $this->cache_kept_days, $this->cache_file_prams);
}
}
PramsFormatter
<?php
namespace library\pingle\cache\config;
class PramsFormatter {
private $cache_file_prams;
public function __construct(Array $prams) {
$this->cache_file_prams = $prams;
$this->formatPrams();
}
public function formatPrams() {
if (is_array($this->cache_file_prams)) {
foreach ($this->cache_file_prams as $k => $value) {
$this->cache_file_prams[$k] = $this->cleanValue($value);
}
}
}
public function cleanValue($value) {
if (is_array($value)) {
throw new \Exception("Array as paramter value is not accepted");
} else {
return str_replace(array(" ", " ", ".", "/", "\\"), "-", $value);
}
}
public function getFormattedPrams() {
return $this->cache_file_prams;
}
}
использование
$cache_config = new CacheConfigurator(array('carrier_id' => $invoicedata['carrier_id'], 'month' => $month, 'year' => $year));
$cache_config->setFunction('Inter-department Calls');
$cache_config->setCacheKeptDays(30);
$cache_config->setCacheDirectory("bin/cache");
$cache = new Cache($cache_config);
if ($cache->check()) {
$node = $cache->read();
} else {
//calculate node
$cache->write($node);
}
Git Repository с улучшенным дизайном
Очень хороший вопрос, но, вероятно, об этом можно написать целую книгу, что затрудняет ответ.
Я бы начал с этого простого вопроса: что лучше описывает кэширование в приведенных ниже вариантах?
Кэширование — это механизм, который позволяет сохранять результат функции в файле в течение нескольких дней, чтобы обеспечить быстрый доступ к нему.
Кэширование — это механизм, который позволяет сохранять результат операции до тех пор, пока соответствующая политика хранения выполняется для обеспечения быстрого доступа к ней.
Ни одно из определений не является идеальным, и это не главное, но я хотел подчеркнуть, что # 1 объясняет кэширование с очень конкретными деталями инфраструктуры, в то время как # 2 определяет механизм более абстрактно.
Надеюсь, теперь вы поняли один из самых больших недостатков вашего дизайна IMO. Различные абстракции неправильны:
Абстракция механизма хранения зависит от конкретной детали инфраструктуры: весь API вращается вокруг файлов. Что делать, если я хотел кэш в памяти?
Алгоритм политики хранения данных очень специфичен: данные будут храниться только определенное количество дней. Что если я захочу выразить кеш в минутах, когда счетчик сбрасывается каждый раз, когда к данным обращаются?
Один совет, который я бы дал вам, — всегда бросать вызов вашим абстракциям и следить за тем, чтобы они не были слишком конкретными, чтобы ваш код мог быть расширяемым и многократно используемым, но не слишком широким. В этом очень помогает внимание на языке проблемной области.
Очевидно, можно сказать гораздо больше, чем это, и иногда зависимость от технологии является правильным выбором, но я думаю, что мой ответ поможет …
Других решений пока нет …