做一件事,无论大小,倘无恒心,是很不好的。而看一切太难,固然能使人无成,但若看得太容易,也能使事情无结果。
前言
那天回顾曾经写的代码,发现当初为了快速完成任务,写了很多if-else的判断,密密麻麻一大串下来,看着着实不大美观而且臃肿,于是便动手使用了策略工厂模式稍微优化一下。这里我就不多赘述工厂模式和策略模式的概念,全网随便一搜很多人都说的很清楚,我只稍微写点简单伪代码,作为一下优化的心得。
新建一个策略接口类InsuranceInterface.php
这个接口类是定义了标准使用方法,之后对接新的保险都继承于该接口类实现
<?php
declare(strict_types=1);
namespace App\Service\ProjectInterface;
interface InsuranceInterface
{
// 计算价格
public function cal(): float;
// 获取险种名字
public function name(): string;
// 获取详情
public function info(): array;
}
继承InsuranceInterface.php完成两个险种的对接
比如我需要对接一个中国人寿(ChinaLife)和对接一个平安保险(PingAn),实现都是随便写写的,具体实现根据业务实际情况完成
新建一个ChinaLifeInsurance.php
<?php
declare(strict_types=1);
namespace App\Service\ProjectInterface;
class ChinaLifeInsurance implements InsuranceInterface
{
public function cal(): float
{
// TODO: Implement cal() method.
return 2;
}
public function info(): array
{
// TODO: Implement info() method.
return [
'money' => 10.5,
'year' => 10,
];
}
public function name(): string
{
// TODO: Implement name() method.
return 'ChinaLifeInsurance';
}
}
新建一个PingAnInsurance.php
<?php
declare(strict_types=1);
namespace App\Service\ProjectInterface;
class PingAnInsurance implements InsuranceInterface
{
public function cal(): float
{
// TODO: Implement cal() method.
return 1;
}
public function info(): array
{
// TODO: Implement info() method.
return [
'money' => 10,
'year' => 10,
];
}
public function name(): string
{
// TODO: Implement name() method.
return 'PingAnInsurance';
}
}
新建一个工厂类InsuranceStrategyFactory.php
这里我是使用了Hyperf框架来写的,因此程序是基于cli模式,内存常驻,所以可以使用单例模式,适当减少内存资源和系统资源的消耗
<?php
declare(strict_types=1);
namespace App\Service\ProjectInterface;
class InsuranceStrategyFactory
{
public static array $instance = [];
/**
* @param $strategyName
* @return InsuranceInterface
*/
public static function getInstance($strategyName): InsuranceInterface
{
/**
* 根据传来的策略名进行拼接,如我们上面的平安保险的实现接口名为PingAnInsurance
* 故$strategyName = PingAn,其他命名空间以及后缀固定拼上即可
*/
$className = __NAMESPACE__ . '\\' . $strategyName . 'Insurance';
// 判断是否已存在该类,存在直接返回,不存在new一个
if (! isset(self::$instance[$strategyName])) {
// 判断类是否存在,不存在抛出异常
if (class_exists($className)) {
$class = new $className();
self::$instance[$strategyName] = $class;
} else {
throw new \RuntimeException('不存在该保险');
}
}
return self::$instance[$strategyName];
}
}
如果你是用传统的php-fpm框架如laravel,thinkPHP这类的,可以不用单例模式,因为PHP是解释性脚本语言,这种运行机制会使每个PHP程序解释执行后,相关资源都被回收,因此单例模式几乎没什么意义。所以可以稍微改造一下,代码如下
<?php
declare(strict_types=1);
namespace App\Service\ProjectInterface;
class StrategyFactory
{
/**
* @param $strategyName
* @return InsuranceInterface
*/
public static function getInstance($strategyName): InsuranceInterface
{
$className = __NAMESPACE__ . '\\' . $strategyName . 'Insurance';
if (class_exists($className)) {
return new $className();
}
throw new \RuntimeException('不存在该保险');
}
}
测试一下代码
新建一个TestController.php
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Service\ProjectInterface\StrategyFactory;
use Hyperf\HttpServer\Annotation\AutoController;
#[AutoController(prefix: 'test')]
class TestController extends AbstractController
{
public function insurance(): \Psr\Http\Message\ResponseInterface
{
try {
// 由工厂类那里可以得知我们insurance_name的值,实际情况需要传什么在接口文档写清楚即可
$insuranceName = $this->request->input('insurance_name');
$insuranceFactory = StrategyFactory::getInstance($insuranceName);
return $this->response->json([
'code' => 1,
'msg' => 'success',
'data' => $insuranceFactory->name(),
]);
} catch (\Throwable $throwable) {
return $this->response->json([
'code' => 0,
'msg' => $throwable->getMessage(),
]);
}
}
}
请求一下接口并附上参数,如我请求http://127.0.0.1:9501/test/insurance?insurance_name=PingAn
,结果如下:
同理请求
http://127.0.0.1:9501/test/insurance?insurance_name=PingAn
假如传递了一个约定外的值
http://127.0.0.1:9501/test/insurance?insurance_name=Foo
,结果如下:
至此,通过策略工厂模式,优化了当时大量对接的险种类型使用的if-else或者switch的代码,使整体代码可阅读性提高,代码更加简洁。当然,如果代码极少发生改变,几乎没有新的类需要继承实现,策略模式只会使程序过于复杂,具体操作需要根据业务需求来定,一切以业务为准。