当前位置: 首页 > news >正文

PHP设计模式策略与适配器实战

PHP设计模式策略与适配器实战

策略模式和适配器模式在实际项目中非常常用。策略模式让你能动态切换算法,适配器模式让不兼容的接口能协同工作。今天就用实战例子说明这两种模式。

策略模式把算法封装成独立的类,客户端可以根据需要选择不同的算法。最常见的例子是支付方式的选择。

```php
interface PaymentGateway
{
public function pay(float $amount, array $params = []): array;
public function refund(string $transactionId): array;
public function getName(): string;
}

class AlipayGateway implements PaymentGateway
{
private string $appId;
private string $privateKey;

public function __construct(string $appId, string $privateKey)
{
$this->appId = $appId;
$this->privateKey = $privateKey;
}

public function pay(float $amount, array $params = []): array
{
return [
'channel' => 'alipay',
'trade_no' => 'ALI' . date('Ymd') . uniqid(),
'amount' => $amount,
'status' => 'success',
'timestamp' => date('Y-m-d H:i:s'),
];
}

public function refund(string $transactionId): array
{
return [
'channel' => 'alipay',
'refund_no' => 'REF' . $transactionId,
'status' => 'success',
];
}

public function getName(): string { return '支付宝'; }
}

class WechatGateway implements PaymentGateway
{
private string $appId;
private string $mchId;

public function __construct(string $appId, string $mchId)
{
$this->appId = $appId;
$this->mchId = $mchId;
}

public function pay(float $amount, array $params = []): array
{
return [
'channel' => 'wechat',
'trade_no' => 'WX' . date('Ymd') . uniqid(),
'amount' => $amount,
'status' => 'success',
'openid' => $params['openid'] ?? '',
];
}

public function refund(string $transactionId): array
{
return [
'channel' => 'wechat',
'refund_no' => 'REF' . $transactionId,
'status' => 'success',
];
}

public function getName(): string { return '微信支付'; }
}

class PaymentContext
{
private ?PaymentGateway $gateway = null;

public function __construct(PaymentGateway $gateway)
{
$this->gateway = $gateway;
}

public function setGateway(PaymentGateway $gateway): void
{
$this->gateway = $gateway;
}

public function executePayment(float $amount, array $params = []): array
{
if ($this->gateway === null) {
throw new RuntimeException('未设置支付网关');
}

$result = $this->gateway->pay($amount, $params);
$this->logPayment($result);
return $result;
}

private function logPayment(array $result): void
{
$log = sprintf(
"[%s] 支付: %s 交易号: %s 金额: %.2f 状态: %s\n",
date('Y-m-d H:i:s'),
$result['channel'],
$result['trade_no'],
$result['amount'],
$result['status']
);
file_put_contents('/tmp/payments.log', $log, FILE_APPEND);
}
}

$alipay = new AlipayGateway('app123', 'key456');
$payment = new PaymentContext($alipay);
$result = $payment->executePayment(99.99);
print_r($result);

// 切换到微信
$payment->setGateway(new WechatGateway('wx_app', 'mch001'));
$result2 = $payment->executePayment(199.99, ['openid' => 'o12345']);
print_r($result2);
?>
```

数据导出也是策略模式的常见应用。不同的导出格式对应不同的策略。

```php
interface ExportStrategy
{
public function export(array $data): string;
public function getContentType(): string;
public function getExtension(): string;
}

class JsonExport implements ExportStrategy
{
public function export(array $data): string
{
return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

public function getContentType(): string { return 'application/json'; }
public function getExtension(): string { return 'json'; }
}

class CsvExport implements ExportStrategy
{
public function export(array $data): string
{
if (empty($data)) return '';

$output = fopen('php://temp', 'r+');
fputcsv($output, array_keys($data[0]));

foreach ($data as $row) {
fputcsv($output, $row);
}

rewind($output);
$content = stream_get_contents($output);
fclose($output);

return $content;
}

public function getContentType(): string { return 'text/csv'; }
public function getExtension(): string { return 'csv'; }
}

class XmlExport implements ExportStrategy
{
public function export(array $data): string
{
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true;
$root = $doc->createElement('data');

foreach ($data as $item) {
$element = $doc->createElement('item');
foreach ($item as $key => $value) {
$child = $doc->createElement($key);
$child->appendChild($doc->createTextNode((string)$value));
$element->appendChild($child);
}
$root->appendChild($element);
}

$doc->appendChild($root);
return $doc->saveXML();
}

public function getContentType(): string { return 'application/xml'; }
public function getExtension(): string { return 'xml'; }
}

class DataExporter
{
private ExportStrategy $strategy;

public function __construct(ExportStrategy $strategy)
{
$this->strategy = $strategy;
}

public function export(array $data, string $filename): void
{
$content = $this->strategy->export($data);
$fullFilename = $filename . '.' . $this->strategy->getExtension();

header('Content-Type: ' . $this->strategy->getContentType());
header('Content-Disposition: attachment; filename="' . $fullFilename . '"');
header('Content-Length: ' . strlen($content));
echo $content;
}

public function exportToFile(array $data, string $path): string
{
$content = $this->strategy->export($data);
$fullPath = $path . '.' . $this->strategy->getExtension();
file_put_contents($fullPath, $content);
return $fullPath;
}
}

$data = [
['name' => '张三', 'age' => 28, 'email' => 'zhangsan@test.com'],
['name' => '李四', 'age' => 35, 'email' => 'lisi@test.com'],
];

$exporter = new DataExporter(new JsonExport());
$path = $exporter->exportToFile($data, '/tmp/export');
echo "导出到: $path\n";

$exporter2 = new DataExporter(new CsvExport());
$path2 = $exporter2->exportToFile($data, '/tmp/export');
echo "导出到: $path2\n";
?>
```

适配器模式用来让不兼容的接口协同工作。在整合第三方库时特别有用。

```php
// 目标接口
interface LogInterface
{
public function info(string $message): void;
public function error(string $message): void;
public function warning(string $message): void;
}

// 旧系统实现的日志
class SimpleFileLogger
{
private string $path;

public function __construct(string $path)
{
$this->path = $path;
}

public function write(string $level, string $message): void
{
$line = sprintf(
"[%s] %s: %s\n",
date('Y-m-d H:i:s'),
$level,
$message
);
file_put_contents($this->path, $line, FILE_APPEND);
}
}

// 适配器
class SimpleLoggerAdapter implements LogInterface
{
private SimpleFileLogger $logger;

public function __construct(SimpleFileLogger $logger)
{
$this->logger = $logger;
}

public function info(string $message): void
{
$this->logger->write('INFO', $message);
}

public function error(string $message): void
{
$this->logger->write('ERROR', $message);
}

public function warning(string $message): void
{
$this->logger->write('WARNING', $message);
}
}

$logger = new SimpleLoggerAdapter(new SimpleFileLogger('/tmp/app.log'));
$logger->info('用户登录成功');
$logger->error('数据库连接失败');
echo "日志已写入\n";
?>
```

策略模式和适配器模式的核心思想都是面向接口编程。策略模式是"有不同的实现,根据情况选用",适配器模式是"接口不兼容,包一层转接"。理解了这两种模式,代码的扩展性和灵活性会提升很多。

http://www.jsqmd.com/news/948266/

相关文章:

  • 手机靓号平台哪家正规?4项资质标准对照 - 资讯快报
  • 3分钟掌握洛雪音乐助手:跨平台音乐聚合播放的终极指南 [特殊字符]
  • 从一道CTF题看PHP Session反序列化:手把手教你复现HarekazeCTF2019的Easy Notes
  • 气井井口压力已知时快速推算井底流压的MATLAB工具集
  • 3个现代Anki模板主题:如何让记忆卡片变得美观又高效
  • GLM-5.1办公语义理解器:让AI真正读懂任务意图与组织规则
  • WeChatExporter:永久保存你的微信聊天记忆
  • 实战应用:基于快马平台开发功能模拟版河南移动iptv
  • 东营威固官方授权门店推荐:柏年超群北二路旗舰店专业贴膜 守护行车品质 - 速递信息
  • VC6环境下用MFC开发的纯文本通讯录工具,带完整增删查改功能和源码
  • 2026 哈尔滨本地手表回收哪家靠谱?四大维度盘点五大回收门店 - 奢侈品交易观察员
  • DLSS状态指示器终极指南:如何轻松监控游戏AI超分辨率性能
  • 零基础自学网安总找不到靠谱资料?完整自学步骤全梳理,配套对应系统视频教程 + 详细学习笔记,告别碎片化学习,新手少走半年弯路
  • 动态目标无缝追踪技术白皮书
  • 3步掌握WebPlotDigitizer:从图表图像到结构化数据的思维革命
  • Jina Embeddings v2 Base DE常见问题解答:解决使用中的15个典型问题
  • WBench-weights核心模型详解:CLIP、DINOv2、Qwen2-VL等15个模型的完整对比
  • 2026多模型协同工作流:从Claude 4.6到MetaChat的智能调度实践
  • 即梦去水印保存怎么还有水印?实测这3种方法100%有效(附免费工具) - 科技热点发布
  • WebPlotDigitizer:3步将科研图表数据智能提取为Excel表格
  • Paperxie:跳出改写套路,在知网维普 AIGC 新规下解锁论文双指标优化新解法
  • 非科班零基础也能逆袭?详解网安年薪百万实现逻辑,从入门知识点到项目实战、大厂求职完整落地指南,转行收藏这一篇就足够
  • 手机号定位查询系统:3秒快速定位手机号归属地,地图直观展示
  • 车辆动力总成六自由度振动优化Matlab实操包(含调试通过代码、仿真图与参数设置指南)
  • Steam成就管理终极指南:如何使用SAM快速解锁你的游戏成就
  • 3步搞定LaTeX公式转换:LaTeX2Word-Equation完全指南
  • LLaMA.cpp生态新成员:BitCPM4-CANN-8B-gguf本地运行与优化技巧
  • 别再到处找教程了!JDK 1.8/11/17下keytool操作证书的保姆级命令手册(含Windows/Linux路径差异)
  • 淡纹抗初老眼油哪款好?实测4款高性价比眼油直击眼周干纹黑眼圈 - 全网最美
  • 除了网卡,DPDK还能加速什么?手把手配置加密引擎和基带加速器