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 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 6

代码还有改进的空间

1年前 评论
yyy123456 (楼主) 1年前
yyy123456 (楼主) 1年前
yyy123456 (楼主) 1年前

太繁琐了 写个业务代码用不到interface

1年前 评论
yyy123456 (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
50
粉丝
7
喜欢
60
收藏
94
排名:592
访问:1.3 万
私信
所有博文
社区赞助商