首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 設計模式:策略模式實作快取儲存策略切換

     編輯:PHP 綜合知識

策略模式:封裝演算法族群,執行時動態切換

策略模式 定義一系列演算法,將每個演算法封裝起來,並使它們可互換。本文以「快取儲存策略」為例,示範 File、Redis、APCu、Database 多種儲存後端的無縫切換。

核心介面與實作

<?php
// 1. 策略介面
interface CacheStrategy {
    public function get(string $key): mixed;
    public function set(string $key, mixed $value, int $ttl = 3600): bool;
    public function delete(string $key): bool;
    public function clear(): bool;
    public function has(string $key): bool;
}

// 2. 檔案快取策略
class FileCacheStrategy implements CacheStrategy {
    private string $cacheDir;
    
    public function __construct(string $cacheDir = '/tmp/cache') {
        $this->cacheDir = $cacheDir;
        if (!is_dir($cacheDir)) mkdir($cacheDir, 0755, true);
    }
    
    private function filePath(string $key): string {
        return $this->cacheDir . '/' . md5($key) . '.cache';
    }
    
    public function get(string $key): mixed {
        $file = $this->filePath($key);
        if (!file_exists($file)) return null;
        
        $data = unserialize(file_get_contents($file));
        if ($data['expire'] < time()) {
            unlink($file);
            return null;
        }
        return $data['value'];
    }
    
    public function set(string $key, mixed $value, int $ttl = 3600): bool {
        $data = ['value' => $value, 'expire' => time() + $ttl];
        return file_put_contents($this->filePath($key), serialize($data)) !== false;
    }
    
    public function delete(string $key): bool {
        $file = $this->filePath($key);
        return file_exists($file) ? unlink($file) : true;
    }
    
    public function clear(): bool {
        $files = glob($this->cacheDir . '/*.cache');
        foreach ($files as $file) unlink($file);
        return true;
    }
    
    public function has(string $key): bool {
        return $this->get($key) !== null;
    }
}

// 3. Redis 快取策略
class RedisCacheStrategy implements CacheStrategy {
    private Redis $redis;
    private string $prefix;
    
    public function __construct(Redis $redis, string $prefix = 'cache:') {
        $this->redis = $redis;
        $this->prefix = $prefix;
    }
    
    public function get(string $key): mixed {
        $data = $this->redis->get($this->prefix . $key);
        return $data ? unserialize($data) : null;
    }
    
    public function set(string $key, mixed $value, int $ttl = 3600): bool {
        return $this->redis->setex($this->prefix . $key, $ttl, serialize($value));
    }
    
    public function delete(string $key): bool {
        return $this->redis->del($this->prefix . $key) > 0;
    }
    
    public function clear(): bool {
        $keys = $this->redis->keys($this->prefix . '*');
        return $keys ? $this->redis->del(...$keys) > 0 : true;
    }
    
    public function has(string $key): bool {
        return $this->redis->exists($this->prefix . $key);
    }
}

// 4. APCu 快取策略 (單機高效能)
class ApcuCacheStrategy implements CacheStrategy {
    private string $prefix;
    
    public function __construct(string $prefix = 'cache:') {
        if (!extension_loaded('apcu')) throw new RuntimeException('APCu extension required');
        $this->prefix = $prefix;
    }
    
    public function get(string $key): mixed {
        return apcu_fetch($this->prefix . $key);
    }
    
    public function set(string $key, mixed $value, int $ttl = 3600): bool {
        return apcu_store($this->prefix . $key, $value, $ttl);
    }
    
    public function delete(string $key): bool {
        return apcu_delete($this->prefix . $key);
    }
    
    public function clear(): bool {
        // APCu 無前綴清除,需遍歷 (效能較差)
        $iterator = new APCUIterator('/^' . preg_quote($this->prefix, '/') . '/');
        foreach ($iterator as $key => $val) apcu_delete($key);
        return true;
    }
    
    public function has(string $key): bool {
        return apcu_exists($this->prefix . $key);
    }
}
?>

環境類別:統一快取介面

<?php
class CacheManager {
    private CacheStrategy $strategy;
    
    public function __construct(CacheStrategy $strategy) {
        $this->strategy = $strategy;
    }
    
    public function setStrategy(CacheStrategy $strategy): void {
        $this->strategy = $strategy;
    }
    
    // 代理方法
    public function get(string $key): mixed { return $this->strategy->get($key); }
    public function set(string $key, mixed $value, int $ttl = 3600): bool { return $this->strategy->set($key, $value, $ttl); }
    public function delete(string $key): bool { return $this->strategy->delete($key); }
    public function clear(): bool { return $this->strategy->clear(); }
    public function has(string $key): bool { return $this->strategy->has($key); }
    
    // 進階:記憶體快取 + 持久化快取雙層
    public function getWithFallback(string $key, CacheStrategy $fallback): mixed {
        $value = $this->strategy->get($key);
        if ($value === null) {
            $value = $fallback->get($key);
            if ($value !== null) {
                $this->strategy->set($key, $value); // 回填熱點
            }
        }
        return $value;
    }
}
?>

工廠:根據配置建立策略

<?php
class CacheStrategyFactory {
    public static function create(string $driver, array $config = []): CacheStrategy {
        return match($driver) {
            'file' => new FileCacheStrategy($config['path'] ?? '/tmp/cache'),
            'redis' => new RedisCacheStrategy(
                (fn() => { $r = new Redis(); $r->connect($config['host']??'127.0.0.1', $config['port']??6379); return $r; })(),
                $config['prefix'] ?? 'cache:'
            ),
            'apcu' => new ApcuCacheStrategy($config['prefix'] ?? 'cache:'),
            'database' => new DatabaseCacheStrategy($config['pdo']), // 需自行實作
            'array' => new ArrayCacheStrategy(), // 單請求生命週期
            default => throw new InvalidArgumentException("Unknown cache driver: $driver"),
        };
    }
}

// 配置檔案 config/cache.php
return [
    'default' => env('CACHE_DRIVER', 'file'),
    'stores' => [
        'file' => ['driver' => 'file', 'path' => storage_path('framework/cache')],
        'redis' => ['driver' => 'redis', 'host' => '127.0.0.1', 'port' => 6379],
        'apcu' => ['driver' => 'apcu'],
    ],
];

// 應用啟動時
$config = require 'config/cache.php';
$cache = new CacheManager(CacheStrategyFactory::create($config['default'], $config['stores'][$config['default']] ?? []));
?>

使用範例:動態切換策略

<?php
// 1. 一般使用
$cache->set('user:1', ['name' => 'Alice', 'email' => 'alice@example.com'], 1800);
$user = $cache->get('user:1');

// 2. 運行時切換 (如 Redis 維護期間降級到檔案)
if ($redisDown) {
    $cache->setStrategy(new FileCacheStrategy('/var/cache/fallback'));
}

// 3. 雙層快取:APCu (L1) + Redis (L2)
$l1 = new ApcuCacheStrategy('l1:');
$l2 = new RedisCacheStrategy($redis, 'l2:');
$value = $cache->getWithFallback('hot_key', $l2); // 先查 L1,無則查 L2 並回填 L1

// 4. 多策略組合:標籤式快取清除
interface TaggableCacheStrategy extends CacheStrategy {
    public function setWithTags(string $key, mixed $value, array $tags, int $ttl = 3600): bool;
    public function invalidateByTag(string $tag): bool;
}
// 實作需維護 tag->keys 映射
?>

策略模式在 Laravel 中的應用

<?php
// Laravel 契約
interface Store extends CacheStrategy { /* ... */ }

// 服務提供者註冊
class CacheServiceProvider extends ServiceProvider {
    public function register(): void {
        $this->app->singleton('cache', function($app) {
            return new CacheManager($app);
        });
    }
}

// CacheManager 內建策略切換
$cache = app('cache');
$cache->store('redis')->put('key', 'value', 600);
$cache->store('file')->put('key', 'value', 600);

// 動態切換
$cache->extend('custom', fn($app) => new CustomCacheStrategy(...));
$cache->store('custom')->put('key', 'value');
?>

策略模式優缺點總結

優點 缺點
演算法封裝隔離,易於單元測試 策略類別增多 (每個演算法一類)
運行時動態切換,無需修改客戶端 客戶端需知道策略差異 (如 TTL 支援度)
符合開放封閉原則:新增策略不修改既有代碼 簡單情況下引入過度設計
避免條件判斷 (if/else/switch) 散落業務邏輯 策略間共享狀態需額外機制

適用場景判斷

  • ✅ 同一行為有多種實現方式 (排序、壓縮、快取、支付、路由)
  • ✅ 需根據條件 (配置、用戶、環境) 動態選擇實現
  • ✅ 想將複雜條件邏輯從核心類別移出
  • ❌ 只有一種固定實現、不會變更
  • ❌ 策略間差異極大、介面難以統一
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved