Lavavel 面向对象学习 - 代码修改后的面向对象风格
Lavavel 面向对象学习 - 代码修改后的面向对象风格
需求分析
因为消息模板实际有多个的。业务进度消息只是其中一个模板。
控制器类 WechatPushController.php
返回给前端的公共对象类 ResultStandard.php
消息类公共接口 InterfaceMessage.php
业务进度消息类 MessageBusinessProgress.php
微信推送消息类 WechatPush.php
用户 openid 查找类 UserIdToOpenId.php
代码真实可用。
修改后的代码
控制器类 WechatPushController.php
class WechatPushController extends Controller
{
// 控制器方法,发送模板消息。
public function template_message(Request $request, MessageBusinessProgress $message)
{
$user_id = $request->input('user_id');
$message->setMessageTitle($request->input('message_title'));
$message->setMessageOrderNo($request->input('message_order_no'));
$message->setMessageContent($request->input('message_content'));
$message->setMessageRemark($request->input('message_remark'));
$json_result = $message->push($user_id);
return response()->json( $json_result->to_json() )->setEncodingOptions(JSON_UNESCAPED_UNICODE);
}
}
返回给前端的公共对象类 ResultStandard.php
<?php
namespace App\Services;
/**
* 公共结果对象
*
* @author x
*/
class ResultStandard
{
const RIGHT_CODE =200;
protected $code;
protected $message;
protected $data;
public function __construct(bool $boo, $data=[], $message='')
{
if ($boo) {
$this->code = self::RIGHT_CODE;
}
$this->data = $data;
$this->message=$message;
}
public static function get_instance(bool $boo, $data=[], $message='')
{
return new self($boo, $data, $message);
}
public function to_json()
{
if ($this->code == self::RIGHT_CODE ){
return [
'code' => $this->code,
'errMsg' =>'成功',
'data'=> $this->data,
];
}
return [
'code' => $this->code,
'errMsg' =>$this->message ?: '错误',
'data'=> $this->data ?: [],
];
}
}
消息类公共接口 InterfaceMessage.php
<?php
namespace App\Services\ThirdApi\Wechat;
use App\Services\ResultStandard;
/**
* 业务进度消息类公共接口
*
* @author 11
*/
interface InterfaceMessage
{
// 获取业务进程消息的模板id
public function get_template_id():string ;
// 微信推送
public function push($user_id): ResultStandard;
//收集推送的数据。
public function get_push_data():array;
}
业务进度消息类 MessageBusinessProgress.php
<?php
namespace App\Services\ThirdApi\Wechat;
use App\Services\ResultStandard;
/**
* 业务进度消息类
*
* @author 11
*/
class MessageBusinessProgress implements InterfaceMessage
{
const TEMPLATE_ID = 'f9it7NnvjG1XyxtRCGoFo-V7u96TIhT1ZP2yUfE-zNs';
protected $message_order_no; // 订单号
protected $message_title; // 标题
protected $message_content; // 消息主体内容。控制在80个汉字以内比较好
protected $message_remark; // 简短的备注
private $servicePush;
private $serviceUser;
public function __construct()
{
$this->servicePush = new WechatPush();
$this->serviceUser = new UserIdToOpenId();
}
// 获取业务进程消息的模板id
public function get_template_id():string
{
return self::TEMPLATE_ID;
}
// 微信推送
public function push($user_id): ResultStandard
{
$user_id = intval($user_id);
$openid = $this->serviceUser->find_openid($user_id);
if (!$openid) {
return ResultStandard::get_instance(false, [], $this->serviceUser->get_err());
}
try {
$this->servicePush->push($this, $openid);
} catch (\Exception $e) {
return ResultStandard::get_instance(false, [], $e->getMessage());
}
return ResultStandard::get_instance(true);
}
public function get_push_data():array
{
return [
'first' => $this->getMessageTitle(),
'keyword1' => $this->getMessageOrderNo(),
'keyword2' => $this->getMessageContent(),
'keyword3' => date('Y-m-d H:i:s'),
'remark' => $this->getMessageRemark(),
];
}
/**
* @return mixed
*/
public function getMessageOrderNo()
{
return $this->message_order_no;
}
/**
* @param mixed $message_order_no
*/
public function setMessageOrderNo($message_order_no): void
{
$this->message_order_no = $message_order_no;
}
/**
* @return mixed
*/
public function getMessageTitle()
{
return $this->message_title;
}
/**
* @param mixed $message_title
*/
public function setMessageTitle($message_title): void
{
$this->message_title = $message_title;
}
/**
* @return mixed
*/
public function getMessageContent()
{
return $this->message_content;
}
/**
* @param mixed $message_content
*/
public function setMessageContent($message_content): void
{
$this->message_content = $message_content;
}
/**
* @return mixed
*/
public function getMessageRemark()
{
return $this->message_remark;
}
/**
* @param mixed $message_remark
*/
public function setMessageRemark($message_remark): void
{
$this->message_remark = $message_remark;
}
}
微信推送消息类
<?php
namespace App\Services\ThirdApi\Wechat;
use App\Lib\Err;
use EasyWeChat\Factory;
/**
* 微信推送
*
* @author 11
*/
class WechatPush
{
use Err;
protected $app;
public function __construct()
{
$config = [
'app_id' => config('yansongda.wechat.app_id'),
'secret' => config('yansongda.wechat_secret'),
'response_type' => 'array',
];
$this->app = Factory::officialAccount($config);
}
/*
* 推送微信模板消息
*
* //{{first.DATA}}
* //业务单号:{{keyword1.DATA}}
* //办理进度:{{keyword2.DATA}}
* //时间:{{keyword3.DATA}}
* //{{remark.DATA}}
*
* @param MessageBusinessProgress $message
* @param string $openid
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function push(InterfaceMessage $message, string $openid)
{
$result = $this->app->template_message->send([
'touser' => $openid,
'template_id' => $message->get_template_id(),
'url' => '',
'miniprogram' => [],
'data' => $message->get_push_data(),
]);
return $result;
}
}
用户 openid 查找类 UserIdToOpenId.php
<?php
namespace App\Services\ThirdApi\Wechat;
use App\Lib\Err;
use App\Models\User;
use App\Models\UserWx;
/**
* 处理http.
*
* @author 11
*/
class UserIdToOpenId
{
use Err;
public function find_openid($user_id) : ?string
{
$user = User::find($user_id);
if ($user && $user->union_id) {
$wx_record = UserWx::query()->where('unionid', $user->union_id)->first();
if ($wx_record && $wx_record->openid ) {
return $wx_record->openid;
}else {
$this->set_err('用户未关注公司微信公众号,或关注记录没有保存openid字段');
}
}else {
$this->set_err('用户不存在,或用户没有union_id');
}
return null;
}
}
总结
1、修改后,可维护性极大提高,总共也不知道多少行了,花去的时间至少是写修改前代码的 3 倍不止。
2、从代码中可以看出,每个类功能单一,这是 solid 原则的最基本原则,已经实现。
3、具体过程就是分析出有哪些对象,然后让对象之间互相交互。
4、面向对象就是先写出高层代码,不考量具体实现,假定有一个类能够实现。
5、然后把任务分解为一个个的类,直到不能继续分解任务了,再考量如何实现。
6、比如当前这个需要,可以构造一个message 的具体类,里面有对应这个消息模板的字段,然后这个类有个方法叫做push,参数是user_id。为了set和get写的方便,我还用了phpstorm的功能:code - generate - getter and setter
7、这个就是最核心的抽象,想象这个类非常聪明,它竟然能自己把自己发送出去,这样就可以了。
8、为了实现这个目标,给这个类加成员变量,是实际发送消息的类,这样就真的自己可以发送自己了。具体方式就是把自己传给实际发送消息的类。
9、之前还需要把 user_id 转变成 openid,不要自己实现,必须再把任务分配给其他类,保持单一职责。
10、而为了能让实际发送消息类能不需要自己去分辨模板有哪些字段,可以让消息类自己有个拼凑发送字段的方法。
11、实际上,获取openid的类也可以再修改,比如它的目的是获取openid,所以可以有个方法是get_open_id,然后构造方法带一个用户id这样。
12、对比一下修改前的代码就可以知道,这里经过了大量的思考,大量的修改。当然消耗的时间也是成数倍增长的。
13、修改之后的代码是可以写单元测试的。这里可以 mock 微信的返回格式来测试其余的代码。
14、之前的过程代码就很难测试。因为访问了实际的微信服务器。
15、所以,为了让自己的代码通过单元测试,你只能精心的进行面向对象编程。
16、反过来,不在乎单元测试,则各种可能都有,代码可能好,也可能不好,因为没人强制。而只要为了早下班,不加班,同时催的急,就只能面向过程了,毕竟简单直接,无需思考各种对象交互。也就是修改前的代码。
17、其实只要多写多练多实践,面向对象也就慢慢习惯了,并没有特别难以理解的地方。
本作品采用《CC 协议》,转载必须注明作者和本文链接
代码还有改进的空间
太繁琐了 写个业务代码用不到interface