2014-09-05
最近优化php项目,记录下经验,直接上干活。。。
php在公司项目中主要用于页面展现,前端有个view,view向后端的service请求数据,数据的传输格式是json。下面看优化前的service的代码:
[php]
<?php
require_once('../../../global.php');
require_once(INCLUDE_PATH . '/discache/CacherManager.php');
require_once(INCLUDE_PATH.'/oracle_oci.php');
require_once(INCLUDE_PATH.'/caihui/cwsd.php');
header('Content-type: text/plain; charset=utf-8');
$max_age = isset($_GET['max-age']) ? $_GET['max-age']*1 : 15*60;
if($max_age < 30) {
$max_age = 30;
}
header('Cache-Control: max-age='.$max_age);
// 通过将url进行hash作为缓冲key
$url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$url_hash = md5($url);
//echo "/finance/hs/marketdata/segment/${url_hash}.json";
if (!CacherManager::cachePageStart(CACHER_MONGO, "/finance/hs/marketdata/segment/${url_hash}.json", 60*60)) {
// 查询条件
$page = isset($_GET['page']) ? $_GET['page']*1 : 0;
$count = isset($_GET['count']) ? $_GET['count']*1 : 30;
$type = isset($_GET['type']) ? $_GET['type'] : 'query';
$sort = isset($_GET['sort']) ? $_GET['sort'] : 'symbol';
$order = isset($_GET['order']) ? $_GET['order'] : 'desc';
$callback = isset($_GET['callback']) ? $_GET['callback'] : null;
$fieldsstring = isset($_GET['fields']) ? $_GET['fields'] : null;
$querystring = isset($_GET['query']) ? $_GET['query'] : null;
$symbol=isset($_GET['symbol'])?$_GET['symbol']:'';
$date=isset($_GET['date'])?$_GET['date']:'';
if ($type == 'query') {
$queryObj = preg_split('/:|;/', $querystring, -1);
for($i=0; $i<count($queryObj); $i=$i+2){
if(emptyempty($queryObj[$i])) continue;
if($queryObj[$i]=='symbol'){
$symbol = $queryObj[$i+1];
}
if($queryObj[$i]=='date'){
$date = $queryObj[$i+1];
}
}
}
// 查询列表
$oci = ntes_get_caihui_oci();
$stocklist = array();
$cwsd = new namespace/dao/caihui/Cwsd($oci);
$stockcurror = $cwsd->getCznlList($symbol,$date,$sort,$order,$count*($page),$count);
$sumrecords=$cwsd->getRecordCount($symbol,$date);
$i=0;
//var_dump($symbol,$date,$sort,$order,$count*($page),$count);
foreach($stockcurror as $item){
$item['RSMFRATIO1422']=isset($item['RSMFRATIO1422'])?number_format($item['RSMFRATIO1422'],2).'%':'--';
$item['RSMFRATIO1822']=isset($item['RSMFRATIO1822'])?number_format($item['RSMFRATIO1822'],2).'%':'--';
$item['RSMFRATIO22']=isset($item['RSMFRATIO22'])?number_format($item['RSMFRATIO22'],2).'%':'--';
$item['RSMFRATIO10']=isset($item['RSMFRATIO10'])?number_format($item['RSMFRATIO10'],2):'--';
$item['RSMFRATIO12']=isset($item['RSMFRATIO12'])?number_format($item['RSMFRATIO12'],2):'--';
$item['RSMFRATIO4']=isset($item['RSMFRATIO4'])?number_format($item['RSMFRATIO4'],2):'--';
$item['RSMFRATIO18']=isset($item['RSMFRATIO18'])?number_format($item['RSMFRATIO18'],2):'--';
$item['RSMFRATIO14']=isset($item['RSMFRATIO14'])?number_format($item['RSMFRATIO14'],2):'--';
$item['CODE']=$item['EXCHANGE'].$item['SYMBOL'];
//$item['REPORTDATE']=isset($item['REPORTDATE'])?$item['REPORTDATE']:'--';
$stocklist[$i] = $item;
$i=$i+1;
}
// 输出结果
$result = array();
// 页码page、每页数量count、结果总数total、分页数pagecount、结果列表list
$result['page'] = $page;
$result['count'] = $count;
$result['order'] = $order;
$result['total'] = $i;//$stockcurror->count();
$result['pagecount'] = ceil($sumrecords['SUMRECORD']/$count);
$result['time'] = date('Y-m-d H:i:s');
$result['list'] = $stocklist;
if(emptyempty($callback)){
echo json_encode($result);
}else{
echo $callback.'('.json_encode($result).');';
}
CacherManager::cachePageEnd();
}
?>
下面看一下这个service具体完成的功能:
1. 6-16行,准备缓存参数,开启缓存。
2. 19-41行,提取请求参数。
3. 44-49行,连接、查询数据库。
4. 50-67行,将数据库查询结果放入数组。
5. 71-84行,准备json数据。
6. 86-87行,关闭缓存。
如果只看这一个文件,存在的问题有:
1. 19-86行,没有缩进。
2. 44行,每次请求都会重新连接数据库。
3. 53-61行,重复的逻辑,可以提取为一个函数,然后通过迭代完成。
如果大部分后端Service都采用这个结构,那么问题就是所有的Service都需要经过:开启缓存,取参,获取数据,json转化,关闭缓存这一系列的过程。而在所有过程中,除了获取数据的逻辑,其他的过程都是一样的。在代码中存在着大量的重复逻辑,甚至给人一种“复制-粘贴”的感觉,这严重的违背了DRY原则(Don't Repeat Yourself)。所以,这里需要运用面向对象的思想对其重构。而在我重构的过程中,脑海中始终谨记着一个原则——封装变化原则。所谓封装变化,就是区分系统中不变的和可变的,将可变的进行封装,这样可以很好过应对变化。
通过上面的分析,只有获取数据的逻辑是变化的,其他的逻辑是不变的。所以需要对获取数据的逻辑进行封装,具体的封装方式可以采用继承或组合。我采用的是继承的方式,首先将service的处理过程抽象为:
service(){
startCache();
getParam();
getData(); // 抽象方法,由子类实现
toJson();
closeCache();
}
抽象出ServiceBase类,由子类继承,实现相应的获取数据的逻辑,子类不需要处理其他的取参、缓存等逻辑,这些都被ServiceBase类处理了。
[php]
abstract class ServiceBase {
public function __construct($cache_path, $cache_type, $max_age, $age_explore) {
// 获取请求参数
$this->page = $this->getQueryParamDefault('page', 0, INT);
// 省略其他的获取参数的逻辑
……
// 生成响应
$this->response();
}
/**
*
* 子类实现,返回数组格式的数据
*/
abstract protected function data();
/**
*
* 子类实现,返回所有数据的总数
*/
abstract protected function total();
private function cache() {
$url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$url_hash = md5($url);
$key = $this->cache_path.$url_hash.'.json';
if(!CacherManager::cachePageStart($this->cache_type, $key, $this->age_cache)){
$this->no_cache();
CacherManager::cachePageEnd();
}
}
private function no_cache(){
$data = $this->data();
$total = $this->total();
$this->send_data($data, $total);
}
private function send_data($data, $total){
// 进行json转化,省略具体代码
}
private function response() {
header('Content-type: text/plain; charset=utf-8');
header('Cache-Control: max-age='.$this->age_explore);
if($this->cache_type == NONE || self::$enable_cache == false){
$this->no_cache();
}else{
$this->cache();
}
}
}
这就是各个service的抽象父类,有两个抽象方法data和total,data是返回数组格式的数据,tatol是由于分页加入的。具体的service只要继承ServiceBase并实现data和total方法即可,其他的逻辑都是复用的父类的。实际上,优化后的ServiceBase是使用了模板方法模式(Template Method),父类定义算法处理的流程(service的处理过程),子类实现某个具体变化的步骤(具体service的获取数据的逻辑)。通过使用模板方法模式可以保证步骤的变化对于客户端是透明的,并且可以复用父类中的逻辑。
下面是上述php采用ServiceBase后的代码:
[php]
class CWSDService extends ServiceBase{
function __construct(){
parent::__construct();
$oci = ntes_get_caihui_oci();
$this->$cwsd = new namespace/dao/caihui/Cwsd($oci);
}
public function data(){
$stocklist = array();
$stockcurror = $this->cwsd->getCznlList($this->query_obj['symbol'],
$this->query_obj['symbol'], $sort, $order, $count*($page), $count);
$filter_list = array('RSMFRATIO1422', 'RSMFRATIO1822', 'RSMFRATIO22',
'RSMFRATIO10', 'RSMFRATIO12', 'RSMFRATIO4', 'RSMFRATIO18',
'RSMFRATIO14');
$i=0;
foreach($stockcurror as $item){
foreach($filter_list as $k)
$this->filter($item, $k);
$item['CODE']=$item['EXCHANGE'].$item['SYMBOL'];
$stocklist[$i] = $item;
$i=$i+1;
}
return $stocklist;
}
public function total(){
return $sumrecords=$this->cwsd->getRecordCount($this->query_obj['symbol'],
$this->query_obj['symbol']);
}
private function filter($item, $k){
isset($item[$k])?number_format($item[$k],2).'%':'--';
}
}
new CWSDService('/finance/hs/realtimedata/market/ab', MONGO, 30, 30);
代码量从87减少到32行,是因为大部分的逻辑都由父类完成,具体service只需要关注自己的业务逻辑就可以了。通过上面代码可以看出继承可以实现代码复用,多个子类中的相同的逻辑可以提取到父类中达到复用的目的;同时,继承也增加了父类和子类之间的耦合性,这也就是组合由于继承的方面,如果这个例子采用组合来封装变化,则具体的实现就是策略模式,将具体获取数据的逻辑看成是策略,不同的service就是不同的策略,由于时间原因,不再赘述。。。
摘自 chosen0ne的专栏
1
CI框架连接数据库配置操作以及多数据库操作
09-05
2
asp 简单读取数据表并列出来 ASP如何快速从数据库读取大量数据
05-17
3
C语言关键字及其解释介绍 C语言32个关键字详解
04-05
4
C语言中sizeof是什么意思 c语言里sizeof怎样用法详解
04-26
5
最简单的asp登陆界面代码 asp登陆界面源代码详细介绍
04-12
6
PHP中的魔术方法 :__construct, __destruct , __call, __callStatic,__get, __set, __isset, __unset , __sleep,
09-05
7
PHP中的(++i)前缀自增 和 (i++)后缀自增
09-05
8
PHP中include和require区别之我见
09-05
常用dos命令及语法
2014-09-27
将视频设置为Android手机开机动画的教程
2014-12-11
php递归返回值的问题
2014-09-05
如何安装PHPstorm并配置方法教程 phpstorm安装后要进行哪些配置
2017-05-03
java中的info是什么意思
2022-03-24
PHP 教程之如何使用BLOB存取图片信息实例
2014-09-05
IcePHP框架中的快速后台中的通用CRUD功能框架
2014-09-05
单片机编程好学吗?单片机初学者怎样看懂代码
2022-03-21
PHP数组函数array
2014-09-05
学ug编程如何快速入门?
2022-03-17
斗罗大陆魂师对决手游官方版下载v2.31.1 安卓版
卡牌对战 1.55G
下载坦克大决战游戏下载v1.9462 安卓官方正版
射击枪战 388.5M
下载孤胆车神维加斯官方正版(Gangstar Vegas)下载v8.5.1c 安卓手机版
射击枪战 2.44G
下载孤胆车神维加斯僵尸城版本下载v8.5.1c 安卓版
射击枪战 2.44G
下载英雄联盟云顶之弈手机版本下载v14.8.5768838 安卓最新版本
策略塔防 78.2M
下载苍蓝前线手游下载v1.1.0 安卓版
策略塔防 464.3M
下载崩坏学园2手游下载v12.2.8 安卓最新版本
角色扮演 323.3M
下载彩虹联萌手游下载v7.3.23015 安卓官方版
卡牌对战 804.7M
下载花花填色苹果版下载v2.2.5 iPhone版
下载
奥特曼超时空英雄官方正版下载v1.0.2 安卓版
下载
全民奇迹2ios版下载v15.0.0 iphone版
下载
罗布乐思roblox国际服最新版官方正版下载v2.682.538 安卓版
下载
com.roblox.client国际服下载v2.682.538 安卓版
下载
绝地鸭卫游戏下载v0.2.3 安卓版
下载
梦幻模拟战官服下载v6.13.2110 安卓最新版
下载
天龙八部手游8868版下载v1.133.2.2 安卓版
下载