周梦康 发表于 2014-08-04 4589 次浏览 标签 : 计算机基础

咱们说到程序优化,立马想到的是什么内容呢?一般情况下我们想到的可能是

  1. 程序执行速度
  2. 页面载入时间
  3. 内存占用
  4. 网络流量消耗
  5. 牺牲空间来换取执行速度的提升
  6. 执行重复操作来换取内存消耗的优化
  7. 以及用各种办法减少对数据库的查询

今天我们主要说的是另种优化,那就是代码结构的优化,而这一点往往更加重要:

优化可读性以及可扩展性

  1. 硬件足够,系统复杂的时代,让代码易于阅读和调试,易于维护和扩展更重要。才能更好的团队合作,才能适应需求变化。
  2. 咱们的代码,写1次,调试5次,修改10次,阅读50次。
  3. 连续不断的变化会削弱原设计的概念完整性,从而最终导致软件的死掉。
  4. 我们读代码就是用肉眼和大脑编译执行代码。
  5. 不要让“其他人”读不懂你的代码,“其他人”可能就是一周后的你。
  6. 优化时难以兼顾各个方面,当致力于性能优化时,很可能让应用程序内存消耗增加,同时代码可读性也变差。

优化既对立又统一

优化时难以兼顾各个方面,当致力于性能优化时,很可能让应用程序内存消耗增加,同时代码可读性也变差。

从具体代码分析来谈如何提高代码的可读性

遵守规范

  1. 代码风格统一 pear style: http://pear.php.net/manual/en/standards.indenting.php
  2. 命名规范 http://pear.php.net/manual/en/standards.naming.php
  3. 注释  http://pear.php.net/manual/en/standards.sample.php
  4. 自动生成文档 http://pear.php.net/package/PhpDocumentor

代码结构的优化

  1. 函数或者方法不应过长(定一个标准,比如不超过60行)
  2. 一个函数或者方法只做一件事情。(如果要做两件,把它拆开,同一个变量也不能存两种数据
  3. 删掉死代码
  4. 解决重复性代码
  5. 不写过于高明的代码
  6. 如果代码里有一段逻辑需要注释才能说清楚,果断提出,用有意义的命名
  7. 尽量逻辑清晰

编写模块化代码

  1. 编写模块化代码、面向对象,设计模式
  2. 对外提供所需功能,但是隐藏实现细节
  3. 系统隔离
  4. 编写可移植代码
  5. 扩展功能不改变旧的接口调用,不要随意加参数

一个函数只做一件事演示

/**
 * 一个函数或者方法只做一件事情
 * 下面是的函数的功能是发送短信,但是它却完成里用户电话号码的查询
 * 应该把取用户手机号的逻辑提出
 */
function sendSMS($uid, $msg)
{
	$sql = "select phone from user where uid =".intval($uid);
	$re = M('user')->query($sql);
	$param['OperID'] = 'xxx';
	$param['OperPass'] = 'xxx';
	$param['msg'] = $msg;
	$param['DesMobile'] = $re[0]['phone'];
	$api = 'http://xxxx/QxtSms/QxtFirewall?'.urldecode(http_build_query($param));
	//....调接口发短信
}

//改进,我们工作中是不是也经常忽略这样的问题呢?

function getPhoneByUid($uid)
{
	$sql = "select phone from user where uid =".intval($uid);
	$re = M('user')->query($sql);
	return $re[0]['phone'];
}

function sendSMS($uid, $msg)
{

	$param['OperID'] = 'xxx';
	$param['OperPass'] = 'xxx';
	$param['msg'] = $msg;
	$param['DesMobile'] = getPhoneByUid($uid);
	$api = 'http://xxxx/QxtSms/QxtFirewall?'.urldecode(http_build_query($param));
	//....调接口发短信
}

删掉死代码演示

//1:去除死代码
function deadcode()
{
	setRunLog();
	;//下面是不知死活的代码。。。
}

function setRunLog()
{
	$tree = array_reverse(debug_backtrace());
	$log = '';
	foreach ($tree as $k => $t)
	{
		$log .= $k.": File ".$t['file']."  , function ".$t['function']." , line ".$t['line']." <br />";
	}
	echo date('Y-m-d H:i:s').':I am running by:  <br />'.$log;
}

同一个变量别存两种数据

function addCart($item_id , $count)  
{  
	if(!is_array($item_id))  
	{  
		$_SESSION['cart']['item_id'] = $count;  
	}  
	else  
	{  
		foreach($item_id as $i_id => $count)  
		{  
			$_SESSION['cart']['i_id'] = $count;  
		}  
	}  
}  
addCart( 'IPHONE3' , 2 );  
addCart( array('IPHONE3' => 2 , 'IPAD' => 5) ); 

//更提倡这么写
function addCartSingle($item_id , $count)
{

}

function addCartArr($arr )
{

}

解决重复代码演示

//解决重复性代码
function reg_user()
{
	//insert into user...
	;//注册用户 ...
}


function user_reg()
{
	//insert into user...
	;//用户注册 ...
}

//每次修改要改两处,不然出BUG
//不能贸然删除,有好多地方都调用了。
//改成这样,虽然不完美,至少好一些了

function user_reg()
{
	return reg_user();
}

不要写过于高明的代码演示

//不写过于高明的代码
//2B 程序员
function swap1()
{
	$a = 2;
	$b = 8;
	list($a, $b) = array($b, $a);
	echo "$a , $b <br />";
}
swap1();

//普通程序员
function swap2($a, $b)
{
	list($a, $b) = array($b, $a);
	echo "$a , $b <br />";
}
swap2(2, 8);

//文艺程序员
function swap3($a,$b)
{
    $a=$a^$b;
    $b=$a^$b;
    $a=$a^$b;
	echo "$a , $b <br />";
}
swap3(2, 8);

提取出需要注释的逻辑不清代码

//提出需要注释的逻辑不清的代码
function loadHostConfig()
{
	//这里是取的当前URL的二级域名
	$host = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');
	$arr = explode('.', $host);
	$subdomain = strtolower($arr[0]);
	
	$client_arr = array(
		'edu' => 5,
	);
	if (isset($client_arr[$subdomain]))
	{
		define('UCAI_CLIENT_ID', $client_arr[$subdomain]);
		include_once SITE_PATH.'/config/'.$subdomain.'.class.php';
	}
	else
	{
		include_once SITE_PATH.'/config/www.class.php';
	}
}

//果断提出
//根据函数的意图命名——以它“做什么”命名,而不是以它“怎样做”命名。
function getSubdomain()
{
	$host = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');
	$arr = explode('.', $host);
	$subdomain = strtolower($arr[0]);
	return $subdomain;
}

function loadHostConfig()
{
	//这里是取的当前URL的二级域名
	
	$subdomain = getSubdomain();
	
	$client_arr = array(
		'edu' => 5,
	);
	if (isset($client_arr[$subdomain]))
	{
		define('UCAI_CLIENT_ID', $client_arr[$subdomain]);
		include_once SITE_PATH.'/config/'.$subdomain.'.class.php';
	}
	else
	{
		include_once SITE_PATH.'/config/www.class.php';
	}
}

2014.10.16增加刘丽慷老师的公开课的内容 http://www.ucai.cn/opencourse/176 

昨天听了一节公开课又收获一点也就添加到这篇文章里,api设计的时候需要注意的地方(面向接口编程)

1.参数合理 尽量减少参数数量,不做参数传递,不做参数引用

这么诡异的做法,我很少做,我也想不到,基本上我都是按照上面友林老师的规则来设计的。每个方法确保职能明确,各个关系调用明确,便于理解。

class MC {

    public static function set($key, $value, $expire) {

    }

    public static function get($key) {

    }
    
    public static function bad($input_1, $input_2, &$output) {

        $output     = array(
                        'new values',
                    );

        return  true;

    }
    

}

2.返回值一致 单点返回 类型统一

这一点我在工作中做得不够,我一般都是保证了返回值是单一类型,但是却没有注意单点返回,经常出现多点返回的情况,一旦代码比较长,以后再次梳理起来就会有些困难,不知道到底是在哪返回的了。

class OneReturn {

    public function bad($condition) {

        if ($condition == 1) {
            return  'one';
        }

        if ($condition == 2) {
            return  2;
        }

        return  array(
                    'value' => 3,
                    );

    }

    public function good($condition) {

        $return     = array(
                        'value' => 0,
                    );

        if ($condition == 1) {
            $return['value']    = 1;
        }

        if ($condition == 2) {
            $return['value']    = 1;
        }

        return  $return;

    }


}

摘一段项目中的实际代码做演示

<?php
public static function initZset($r, $k, $objAndFunction)
{
    if ($r->exists($k)) {
        return $r->zCard($k);
    }

    $obj = $objAndFunction[0];
    $func = $objAndFunction[1];
    $params = isset($objAndFunction[2])?$objAndFunction[2]:array();

    $oids = call_user_func(array($obj, $func), $params);

    if (!empty($oids) && is_array($oids)){ 
        $r->multi(Redis::PIPELINE); //事务处理 让(多条)执行命令简单的,更加快速的发送给服务器,但是没有任何原子性的保证
        foreach ($oids as $oid){ 
            $r->zAdd($k, $oid['score'], $oid['value']);
        }
        $r->exec();
        return $r->zCard($k);
    }

    return 0;
}

public static function initZset($r, $k, $objAndFunction)
{
    $return = 0;
    
    if ($r->exists($k)) {

        $return = $r->zCard($k);
        
    }else{

    	$obj = $objAndFunction[0];
	    $func = $objAndFunction[1];
	    $params = isset($objAndFunction[2])?$objAndFunction[2]:array();

	    $oids = call_user_func(array($obj, $func), $params);

	    if (!empty($oids) && is_array($oids)){ 
	        $r->multi(Redis::PIPELINE); //事务处理 让(多条)执行命令简单的,更加快速的发送给服务器,但是没有任何原子性的保证
	        foreach ($oids as $oid){ 
	            $r->zAdd($k, $oid['score'], $oid['value']);
	        }
	        $r->exec();
	        $return = $r->zCard($k);
	    }
    }

    return $return;
}

3.使用 try-catch

(关于为什么要使用try-catch呢?比如我们的项目肯定是很大,相互嵌套调用的次数比较多,如果是在登录成功的基础上才有了后面的各种复杂的操作,一旦登录失败,就可以抛出异常而不需要再到后面对结构各种判断了。)这句话是老师上课的时候说的,我还没有能完全理解消化,感觉如果返回一个标识登录失败,不也一样可以终止吗?

class TryCatch {

    public function login($username, $password) {

        $return     = array(
                            'succ'  => 0,
                            'uid'   => 0,
                        );

        if (!User::checkPassword($username, $password)) {
            throw new Exception('Login failed', $errorCode = 110);
        }

        return  $return;

    }

}

评论列表