有趣生活

当前位置:首页>科技>php内部系统开发 PHP企业级系统开发-大型网站开发范式-7.2

php内部系统开发 PHP企业级系统开发-大型网站开发范式-7.2

发布时间:2026-07-20阅读(1)

导读7.2列表页开发范式在设计模式里,有一个设计思想,“找出变化并封装之”。这一思想所蕴含的意义非常丰富,而且富有启发性。而在敏捷开发中,也推崇拥抱变化,快速响....7.2 列表页开发范式

在设计模式里,有一个设计思想,“找出变化并封装之”。这一思想所蕴含的意义非常丰富,而且富有启发性。而在敏捷开发中,也推崇拥抱变化,快速响应。

知道这些思想的人很多,但真正理解的人很少。能理解这些思想的,并且最终加以实践并持久坚持推行的人则更少。更糟糕的是,实际项目开发过程中,尤其在大型网站系统开发过程中,业务需求以一种无序、突发、紧急的姿态,要求迭代到系统中。在面对来自业务、产品和市场的压力,同时面对内部系统亟待重构和优化这样内外交迫的背景下,软件开发工程师就更加没有时间,也没有精力尝试“找出变化并封装之”这一思想带来的启发了。如果,软件开发工程连这一思想都不知道的话,局势则会更为严峻。

列表页就是一个富含变化的战场。它也是业务上的兵家必争之地。这是由于列表页本身的性质决定的。可以说,列表页是整个网站系统中,访问量最大的页面。更进一步,用户做出选择的地方就是在列表页。对于同一位用户,这是零和选择,如果用户选择了A,他就不会选择B;如果选择了B,就不会再选择C。当然,用户选择的次数通常都会比一次多一点。以电商平台为例,对于琳琅满目的商品,用户通常都会在访问列表页时选择靠前的商品进行浏览,然后做出选择。极少会有用户拉到页面底部查看后面的商品,更不用说一页页翻下去。现在的时代,是一个快节奏的时代,我们往往都需要在短时间内做出抉择。无疑排在前面的选项,会有更大的概率被用户选中,成为幸运儿。一旦选择,进入到详情面,那就是具体信息展示的事情了。

可以毫不夸张地说,列表页在整体产品开发周期中,所占用的时间、资源、精力会远大于其他页面。不管是在电商平台系统,还是在招聘网站系统,抑或是国际租车平台。列表页的需求,一个接一个;改版的计划,一波接一波,足以让开发团队和测试团队应接不暇。

作为软件开发工程师,我们要始终致力于为业务创造更多的价值。那么对于列表页,我们应该怎么做才能支撑源源不断的变化呢?这其中又有哪些开发范式?这些将是本节将要重点讨论的话题。

7.2.1 四大排序与排序微架构

透过业务繁华的表象,列表页的本质上是针对集合的基本操作。对于集合来说,主要有三大操作:排序、筛选和映射。

我们先来探索排序操作,因为这部分是关乎业务核心价值的关键环节。排在前面的元素,更能获取通往终端用户的绿卡。不管是用户,还是供应商、合作伙伴、公司高管、活动方,对列表页的排序都非常关注。因为列表页的排序,极大程度上决定了流量的分配。如果说,得流量者得天下,那么就更能明白为什么大家都对列表页的排序关注度如此之高,如此敏感,足以牵动人们的每一根神经。

列表页的排序,不容小觑。例如搜索引擎的竞价排名。

根据多年的开发经验,列表页的排序,自底往上,分为四大类:

  • 默认的基础排序
  • 二次干预的运营排序
  • 推荐算法的实时排序
  • 特定专题的活动排序

这四类排序,是层层往上的。先有基础的排序,再有干预的排序,接着是推荐算法的排序,最后是针对某些特定元素置顶的操作。下面分别详细说明,以及各类排序的实现要点和开发范式。

7.2.2 默认的基础排序

第一道排序。是默认的基础排序。在数据初次录入时,此基础排序所需要的依据就已经存在了,不管录入方式是手动的还是自动的,是由用户产生的还是供应商制作的。例如用人单位在发布招聘岗位信息时的发布时间,在招聘网站的列表时就可以根据发布时间进行降序排序。

通常,基础排序数据,和业务的基本信息存放在一起,即存放在数据库同一张表里。这里,结合数据库自身的排序,在获取数据时就已经实现了基础排序。这一个环节很简单,也是开发网站的通用技术,并不是我们讨论的重点。

我们关注的是,网站以此基础排序为起点,在不断迭代和演进中,能否友好地支持后面一连串的扩展排序。当预见列表页存在不止一种排序时,我们应当如何设计?既能满足业务的需求,又能减轻开发和维护的成本,还能兼顾调试、排查和快速定位问题。这是一个值得深入思考的问题。如果处理不当,系统将陷入混乱、臃肿,随之而来的是产品人员急于对系统的优化却得不到响应的矛盾激发。

为充实接下来的一系列排序介绍,继续以前面的迷你招聘网站为例,在PHP招聘列表页,有以下基础排序后的招聘岗位数据。

<?php// ./model/JobModel.php 文件class JobModel /** extends Model **/{ public function getJobList($city, $type) { return array( array( id => 21, job_name => PHP软件开发工程师, job_snapshot => 6k-8k / 经验1年以下 / 大专 / 全职, ), array( id => 22, job_name => 高级PHP工程师, job_snapshot => 12k-23k / 经验3-5年 / 本科 / 全职, ), array( id => 23, job_name => 资深PHP开发工程师, job_snapshot => 15k-30k / 经验5-10年 / 本科及以上 / 全职, ), array( id => 24, job_name => PHP程序员, job_snapshot => 4k-5k / 广州 / 经验1年以下 / 大专 / 全职, ), array( id => 25, job_name => 高级PHP开发工程师, job_snapshot => 14k-20k / 广州 / 经验1-3年 / 本科 / 全职, ), ); }}

这是一份从数据库表获取的数据,已经根据发布时间从后到前降序排序。数组中的每一份数据分别表示一个招聘岗位,id表示招聘岗位的ID,job_name表示招聘岗位名称,job_snapshot为招聘岗位的关键描述。而招聘信息详情页的网址格式为:/detail-{招聘岗位ID}.html。

此时,列表页控制器的实现代码如下。假定当前城市为广州。

<?php// ./controller/ListController.php 文件require_once dirname(__FILE__) . /../model/JobModel.php;class ListController { public function show() { $type = $_GET[type]; $city = gz; $jobModel = new JobModel(); $job = $jobModel->getJobList($city, $type); // 视图渲染 include(dirname(__FILE__) . /../view/list.html); }}

这时,列表页的展示效果如下:

图7-3 迷你招聘网站列表页

请注意,这里有一个小技巧和小窍门。为了进行SEO优化,或出于对安全性的考虑,网站会采用伪静态的做法。例如这里的列表页的访问地址是:http://dev.job.com/list-php.html,其访问文件的后缀是html,隐藏了PHP实现这一信息,并且更容易被搜索引擎收录。因为搜索引擎喜欢变化不频繁的页面,而html文件则是静态网页。但是背后是怎么实现的呢?

关键在于Nginx的规则重写。通过使用rewrite,可以将特定规则的链接,重定向到指定的PHP文件进行处理。

server { server_name dev.job.com; …… if (!-e $request_filename) { rewrite ^/list-(.*).html$ /list.php?type=$1 last; }}

做好这些基础准备后,接下来就可以拥抱排序这一富有变化的领域了。

7.2.3 二次干预的运营排序

默认的基础排序,是根据数据最初录入的信息,结合业务自身的规则进行的排序。这一环节也可以包含用户在页面功能操作的排序。接下来是第二部分的排序,即二次干预的运营排序。

出于内部排序的需要,或者是与供应商之间的合作关系,又或者是战略伙伴的要求,对于基础排序后的列表数据,我们要拥有二次干预的能力。比如,根据权重、评分、特定分类进行综合的二次排序。而这些与排序有关的因素,可以通过内部的管理后台或者运营平台进行可视化的管理和操作,以及Excel的导入和导出。这一块倒不是我们讨论的重点,我们关注的是如何有效整合多套排序,快速响应业务的需求。

在这次运营排序中,我们先按平常的开发模式简单实现第一个版本,顺便熟悉多维排序的实现思路。这些基础性的内容,会为下面的排序演进做好铺垫。随后,我们会在迭代推荐算法的实时排序时,对现在的排序体系进行重构,使之更加灵活、优雅、易于扩展。当最后实现活动排序时,我们会回顾单元测试的应用,并重新审视整套排序微架构的设计。

再次根据刚学到的数据模板,先对前面实现的列表页控制器进行局部重构,把招聘岗位列表信息的获取,转移到轻量级数据模板中。驾轻就熟,此次局部重构可以分为三小步。

第一小步,创建列表页查询对象,封装部分所需要的列表页参数。

<?php// ./data/ListQuery.php 文件require_once dirname(__FILE__) . /dataprovider/DataQuery.php;class ListQuery extends DataQuery { // 所在城市,如:gz=广州,sz=深圳 public $city; // 岗位类型,如:php, java public $type; public function __construct() { // 默认城市 $this->city = gz; // 预览时指定的城市 if (!empty($_GET[city])) { $this->city = $_GET[city]; } $this->type = $_GET[type]; parent::__construct(); }}

第二小步,使用轻量级数据模板,实现对招聘岗位列表数据获取与缓存控制的封装。

<?php// ./data/JobListData.php 文件require_once dirname(__FILE__) . /dataprovider/LightWeightDataProvider.php;require_once dirname(__FILE__) . /../model/JobModel.php;class JobListData extends LightWeightDataProvider { protected function doGetData(DataQuery $query, &$trace = ) { // 全球追踪器:数据库 $trace .= D; $model = new JobModel(); return $model->getJobList($query->city, $query->type); } protected function getCacheKey(DataQuery $query) { return job_list_city__ . $query->city . _type_ . $query->type; }}

第三小步,改写控制类的调用,从对原始的参数和Model模型层的使用,切换成查询对象以及数据供给器的搭配应用。

<?php// ./controller/ListController.php require_once dirname(__FILE__) . /../data/ListQuery.php;require_once dirname(__FILE__) . /../data/JobListData.php;class ListController { public function show() { // 列表页查询对象 $listQuery = new ListQuery(); $jobListData = new JobListData(); $job = $jobListData->getData($listQuery); // 视图渲染 include(dirname(__FILE__) . /../view/list.html); }}

完成这三小步的局部重构后,再次访问列表页,功能正常。这里,再稍微延伸说明一下。首先,在重构过程中,不应该同时新增功能,即要么戴着“重构”既有功能的帽子,要么戴着“开发”新增功能的帽子,不应同时戴两顶,否则容易造成思维上的混乱。其次,大家在遇到这些代码时,不要轻易跳过,或者翻过去。我也阅读过不少技术类的书籍,里面也会粘贴很多实现代码,影响了阅读,同时不能把思想更好地表达出来。因此,我对于本书的代码编排,会尽量做到精简,同时又会兼顾内容连贯和表达上的完整性,并且保证关键的信息不会丢失。

刚刚完成的这三小步,对于已经有着实际开发经验并熟悉前面数据供给器设计思路的同学,应该很容易理解。即便不看上面提供的源代码,自己也可以从零到一实现一遍。但重点是,我们要明白完成了这次局部重构之后,我们要做什么。或者说,我们为什么要局部重构?

因为我们要进行二次干预的运营排序。

还记得数据供给器在设计时,提供的回调函数吗?通过这个钩子,我们可以进行额外的操作,一如这里的运营排序。这才是我们的重点。

class JobListData extends LightWeightDataProvider { …… protected function afterGetData(DataQuery $query, &$data) { // TODO:进行运营排序 }}

假设业务需求方面,在对迷你招聘网站的招聘岗位列表进行运营排序时,需要综合考虑两个因素:内部设置的权重,以及招聘岗位的评分。权重由运营部门维护和配置,而评分则是根据岗位的待遇、招聘要求、企业的规模、应聘者的评价等得出的评分,评分越高,表示招聘岗位越优质。权重最大值为100,评分最大值为10。这两个因素的排序规则如下:

  • 规则一:评分大的排前面,评分小的排后面,如果评分相等则再比较权重
  • 规则二:权重大的排前面,权重小的排后面,如果权重相等则保持原来的排序

与此同时,我们的招聘岗位信息补充了评分和权重后的数据如下:

表7-2 带权重和评分的招聘岗位

Copyright © 2024 有趣生活 All Rights Reserve吉ICP备19000289号-5 TXT地图HTML地图XML地图