php教程 php手册 php重构优化一例模板方法模式应用

php重构优化一例模板方法模式应用

Jun 13, 2016 am 10:46 AM
php 최적화 존재하다 애플리케이션 방법 모델 주형 경험 리팩터링 프로젝트

 最近优化php项目,记录下经验,直接上干活。。。

        php在公司项目中主要用于页面展现,前端有个view,view向后端的service请求数据,数据的传输格式是json。下面看优化前的service的代码:

[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     $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         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的专栏
 

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Ubuntu 및 Debian용 PHP 8.4 설치 및 업그레이드 가이드 Ubuntu 및 Debian용 PHP 8.4 설치 및 업그레이드 가이드 Dec 24, 2024 pm 04:42 PM

PHP 8.4는 상당한 양의 기능 중단 및 제거를 통해 몇 가지 새로운 기능, 보안 개선 및 성능 개선을 제공합니다. 이 가이드에서는 Ubuntu, Debian 또는 해당 파생 제품에서 PHP 8.4를 설치하거나 PHP 8.4로 업그레이드하는 방법을 설명합니다.

CakePHP 날짜 및 시간 CakePHP 날짜 및 시간 Sep 10, 2024 pm 05:27 PM

cakephp4에서 날짜와 시간을 다루기 위해 사용 가능한 FrozenTime 클래스를 활용하겠습니다.

CakePHP 토론 CakePHP 토론 Sep 10, 2024 pm 05:28 PM

CakePHP는 PHP용 오픈 소스 프레임워크입니다. 이는 애플리케이션을 훨씬 쉽게 개발, 배포 및 유지 관리할 수 있도록 하기 위한 것입니다. CakePHP는 강력하고 이해하기 쉬운 MVC와 유사한 아키텍처를 기반으로 합니다. 모델, 뷰 및 컨트롤러 gu

CakePHP 파일 업로드 CakePHP 파일 업로드 Sep 10, 2024 pm 05:27 PM

파일 업로드 작업을 위해 양식 도우미를 사용할 것입니다. 다음은 파일 업로드의 예입니다.

CakePHP 유효성 검사기 만들기 CakePHP 유효성 검사기 만들기 Sep 10, 2024 pm 05:26 PM

컨트롤러에 다음 두 줄을 추가하면 유효성 검사기를 만들 수 있습니다.

CakePHP 로깅 CakePHP 로깅 Sep 10, 2024 pm 05:26 PM

CakePHP에 로그인하는 것은 매우 쉬운 작업입니다. 한 가지 기능만 사용하면 됩니다. cronjob과 같은 백그라운드 프로세스에 대해 오류, 예외, 사용자 활동, 사용자가 취한 조치를 기록할 수 있습니다. CakePHP에 데이터를 기록하는 것은 쉽습니다. log() 함수는 다음과 같습니다.

PHP 개발을 위해 Visual Studio Code(VS Code)를 설정하는 방법 PHP 개발을 위해 Visual Studio Code(VS Code)를 설정하는 방법 Dec 20, 2024 am 11:31 AM

VS Code라고도 알려진 Visual Studio Code는 모든 주요 운영 체제에서 사용할 수 있는 무료 소스 코드 편집기 또는 통합 개발 환경(IDE)입니다. 다양한 프로그래밍 언어에 대한 대규모 확장 모음을 통해 VS Code는

CakePHP 빠른 가이드 CakePHP 빠른 가이드 Sep 10, 2024 pm 05:27 PM

CakePHP는 오픈 소스 MVC 프레임워크입니다. 이를 통해 애플리케이션 개발, 배포 및 유지 관리가 훨씬 쉬워집니다. CakePHP에는 가장 일반적인 작업의 과부하를 줄이기 위한 여러 라이브러리가 있습니다.

See all articles