文章目录

前景


有的时候,在编写PHP脚本时,通常会根据当前的情况,比如,某个参数,来判断我应该调用哪个类,哪个变量等。为了实现,我看到过很多糟糕的代码。

if($_param['category'] == self::MAN) {
$person = new Man();
} elseif($_param['category'] == self::WOMAN) {
$person = new Woman();
} elseif(...) {...}

可见,这些IF ELSE充斥在了代码里,真是不爽。如果运用可变变量呢?

$person = new $_param['category']();

当然,上述的例子是利用变量名充当类名(这个专业术语是什么?),不过大同小异。通常这种方式应用于一种叫做委托模式的设计中。

需求


有一个“吃货”数组,里面装满了很多种好吃的。形如:

$foodie[0] = array('apple', 'orange', 'banana'...); // 水果
$foodie[1] = array('chicken', 'beef', 'pork'...); // 肉类
$foodie[2] = array('cake', 'pizza', 'cheese'...); // 甜点
$foodie[3] = array('cola', 'coffee', 'juice'...); // 饮品

她每天最多可以吃15样东东(真能吃~),但是有个要求:如果选择一类的15样,比如水果,那么OK,吃15样水果吧;如果选择其中两样,那么必须两者的比例为1:1;如果三样,则2:2:1;如果四样,则2:2:1:1。
当然,在她吃之前要把这些吃的放到篮子里面($basket)。那么,问题来了,她该如何每天都很轻松的将东东放到篮子里,而不必思考太多的条件呢(去除核心对象中的判决和复杂的功能性)?
是不是这样?

if(count($foodie) == 1) {
for ($n = 0; $n < 15; $n++) {
$basket[] = array_shift($foodie[0]);
}
} elseif(count($foodie) == 2) {
//...
} elseif(count($foodie) == 3) {
//...
} elseif(count($foodie) == 4) {
//...
}

这样岂不是很辛苦,种类不同,就有不同的策略。而且,这种糟糕的代码,影响阅读效率、磁盘空间不说,更重要的是如何扩展呢?如果$foodie多了一种是不是又要写一个elseif?
到这里,你可以尝试的利用php的可变变量来实现这一个动态过程。

实现


for($n = 0; $n < 15; $n++) {
/** 动态获取{$foodie}的键值,以保证分类比例满足需求 **/
$k = getK($foodie, 15, (intval($n) + 1) );
$basket[] = array_shift($foodie[$k]);
}

/**
* 获取k.
*
* 调用该方法动态获取当前的k值,来实现每样食物占食物分类的比例:2:2:1:1。
*
* 策略:
* 分类数 键值 每个键值的出现频率(F) 每个键值的出现次数(若干前者向下取整,若不足,则最后者补充)
* 1 0 1 1 * $n
* 2 0/1 0.5/0.5 0.5 * $n / 0.5 * $n
* 3 0/1/2 0.4/0.4/0.2 0.4 * $n / 0.4 * $n / 0.2 * $n
* 4 0/1/2/3 (2/6)/(2/6)/(1/6)/(1/6) (2/6) * $n / (2/6) * $n / (1/6) * $n / (1/6) *$n
*
* 单元测试:
* _getK(4, 14, 10) 1
* _getK(4, 14, 12) 2
* _getK(4, 14, 14) 3
*
* @version 0.0.1
* @since 0.1.0
*
* @author huxu <admin@ihuxu.com>
*
* @param int $tagCount 食物的分类个数(<=4)
* @param int $count 当前获取的总数据个数($count <= $num)
* @param int $num 当前获取的数据序数
*
* @return int k值
*/
function _getK($tagCount = null, $count = null, $num = null) {

/** 频率数组 **/
$F_1 = array(1);
$F_2 = array(0.5, 0.5);
$F_3 = array(0.4, 0.4, 0.2);
$F_4 = array(2/6, 2/6, 1/6, 1/6);

/** 得到相应的频率数组 **/
$curFName = "F_" . intval($tagCount);
$curF = $ {$curFName};

/**
* 循环变量中用于累计.
*/
$curCount = 0;

foreach($curF as $k => $v) {
$curCount += ceil($v * $count);
if($curCount < $num) {
/** 当前本循环中累计数量小于参数{$num}的情况 **/
continue;
} else {
/** 当前本循环中累计数量大于或者等于参数{$num}的情况**/
return $k;
}
}

}

利用上述方式,当食物分类增多时,增加频率数组即可了。

总结


这种方式不仅可以调用变量和类、方法其实也可以。总结来说,利用这种动态调用的方式,可以减少复杂的逻辑判断,增强程序的耦合性和扩展性。

福利 – TB的美食


既然谈到吃货,看看TB的美食吧~

WeChat_1438406137

哈哈~

WeChat_1438406144

一大盘的肉

WeChat_1438406150

清酒

参考


【追加】 – 2015.08.06


经过测试同学的验证,发现_getK()方法的不合理性,修正后如下。感兴趣的同学可以测试下!

static private function _getK($tagCount = null, $count = null, $num = null) {

/** 频率数组 **/
$F_1 = array(1);
$F_2 = array(0.5, 0.5);
$F_3 = array(0.4, 0.4, 0.2);
$F_4 = array(2/6, 2/6, 1/6, 1/6);

/** 得到相应的频率数组 **/
$curFName = "F_" . intval($tagCount);
$curF = $ {$curFName};

/**
* 循环变量中用于累计.
*/
$curCount = 0;

foreach($curF as $k => $v) {
// 最后一个循环
if( ($k + 1) == $tagCount) return $k;
$_tmp = floor( $v * $count);
// 排除向下取整导致为0的情况
$_tmp == 0 && $_tmp = 1;
$curCount = $curCount + $_tmp;
$_tmp = null;
if($curCount < $num) {
/** 当前本循环中累计数量小于参数{$num}的情况 **/
continue;
} else {
/** 当前本循环中累计数量大于或者等于参数{$num}的情况**/
return $k;
}
}

}
Share:

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.