我为徽章激活写了一个类,现在应该重构了。
有什么好的建议,我应该如何重构它的trigger()
方法?
该类的源代码在github中: https://github.com/heal25/ced/blob/master/models/BadgeActivator.php
有问题的方法:
public function trigger($id, $data = []) {
if (!$this->_uid) $this->setUid(Yii::app()->player->model->uid); //set default uid
$activate = false;
switch ($id) {
//case 'login_1': $activate = true; break;
case 'max_nrg_35': if ($data['energy_max'] >= 35) $activate = true; break;
case 'max_nrg_100': if ($data['energy_max'] >= 100) $activate = true; break;
case 'skill_35': if ($data['skill'] >= 35) $activate = true; break;
case 'skill_100': if ($data['skill'] >= 100) $activate = true; break;
case 'strength_35': if ($data['strength'] >= 35) $activate = true; break;
case 'strength_100': if ($data['strength'] >= 100) $activate = true; break;
case 'energy_drink': $activate = true; break;
case 'level_10': if ($data['level'] >= 10) $activate = true; break;
case 'level_100': if ($data['level'] >= 100) $activate = true; break;
case 'dollar_50': if ($data['dollar'] >= 50) $activate = true; break;
case 'dollar_5000': if ($data['dollar'] >= 5000) $activate = true; break;
case 'travel_loc3': if ($data['water_id'] == 3) $activate = true; break;
case 'travel_county2': if ($data['county_id'] == 2) $activate = true; break;
case 'travel_county9': if ($data['county_id'] == 9) $activate = true; break;
case 'routine_100': if ($data['routine'] >= 100) $activate = true; break;
case 'loc_routine_4b': if ($data['water_id']==4 and $data['routine'] > 0) $activate = true; break;
case 'loc_routine_13s': if ($data['water_id']==13 and $data['routine'] >= 3) $activate = true; break;
case 'loc_routine_28s': if ($data['water_id']==28 and $data['routine'] >= 3) $activate = true; break;
case 'loc_routine_37g': if ($data['water_id']==37 and $data['routine'] >= 9) $activate = true; break;
case 'loc_routine_52b': if ($data['water_id']==52 and $data['routine'] > 0) $activate = true; break;
case 'loc_routine_61s': if ($data['water_id']==61 and $data['routine'] >= 3) $activate = true; break;
case 'loc_routine_71g': if ($data['water_id']==71 and $data['routine'] >= 9) $activate = true; break;
case 'loc_routine_72e': if ($data['water_id']==72 and $data['routine'] >= 27) $activate = true; break;
case 'loc_routine_46d': if ($data['water_id']==46 and $data['routine'] >= 81) $activate = true; break;
case 'setpart_3': if ($data['cnt'] >= 3) $activate = true; break;
case 'setpart_10': if ($data['cnt'] >= 10) $activate = true; break;
case 'setpart_30': if ($data['cnt'] >= 30) $activate = true; break;
case 'first_duel_win': if ($data['role'] == 'caller' and $data['winner'] == 'caller') $activate = true; break;
case 'duel_success_100': if ($data['cnt'] >= 100) $activate = true; break;
case 'duel_fail_100': if ($data['cnt'] >= 100) $activate = true; break;
case 'duel_rate_40': if ($this->getSuccessRate(100, $data) <= 40) $activate = true; break;
case 'duel_rate_25': if ($this->getSuccessRate(300, $data) <= 25) $activate = true; break;
case 'duel_rate_10': if ($this->getSuccessRate(600, $data) <= 10) $activate = true; break;
case 'duel_rate_60': if ($this->getSuccessRate(100, $data) >= 60) $activate = true; break;
case 'duel_rate_75': if ($this->getSuccessRate(300, $data) >= 75) $activate = true; break;
case 'duel_rate_90': if ($this->getSuccessRate(900, $data) >= 90) $activate = true; break;
case 'duel_money_100': if ($data['dollar'] >= 100) $activate = true; break;
case 'duel_money_1000': if ($data['dollar'] >= 1000) $activate = true; break;
case 'duel_win_chance35': if ($data['winner'] and $data['chance'] <= 35) $activate = true; break;
case 'duel_win_chance20': if ($data['winner'] and $data['chance'] <= 20) $activate = true; break;
case 'duel_win_chance5': if ($data['winner'] and $data['chance'] <= 5) $activate = true; break;
case 'duel_lose_chance65': if (!$data['winner'] and $data['chance'] >= 65) $activate = true; break;
case 'duel_lose_chance80': if (!$data['winner'] and $data['chance'] >= 80) $activate = true; break;
case 'duel_lose_chance95': if (!$data['winner'] and $data['chance'] >= 95) $activate = true; break;
case 'duel_2h': if ($data['role'] == 'caller' and date('G')==2) $activate = true; break;
case 'shop_item10': if (Yii::app()->player->model->owned_items >= 10) $activate = true; break;
case 'shop_bait20': if (Yii::app()->player->model->owned_baits >= 20) $activate = true; break;
case 'set_b': if ($data['id']==1) $activate = true; break;
case 'set_s': if ($data['id']==2) $activate = true; break;
case 'set_g': if ($data['id']==3) $activate = true; break;
case 'set_sell_b': if ($data['id']==1) $activate = true; break;
case 'set_sell_s': if ($data['id']==2) $activate = true; break;
case 'set_sell_g': if ($data['id']==3) $activate = true; break;
case 'club_join': $activate = true; break;
case 'club_create': $activate = true; break;
case 'club_members_8': if ($data['cnt'] >= 8) $activate = true; break;
case 'login_days_7': if ($this->getLoginDays() >= 7) $activate = true; break;
case 'login_days_30': if ($this->getLoginDays() >= 30) $activate = true; break;
case 'login_days_60': if ($this->getLoginDays() >= 60) $activate = true; break;
case 'win_contest': $activate = true; break;
}
if ($activate) {
return $this->activate($id);
}
return false;
}
我可以提取'loc_routine_ *'案例的激活,但并不多。
答案 0 :(得分:2)
我不熟悉Yii,但是通过浏览他们的文档,你可能会重构trigger
方法来使用Yii Validators和DI容器。这些方面的东西:
class BadgeActivator
…
public function trigger($id, array $data)
{
$badgeValidator = $this->badgeValidators->findById($id);
if ($badgeValidator->validate($data)) {
$this->activate($id);
}
}
}
BadgeValidators
对象是实现BadgeValidator
接口的简单对象集合。您需要将此注入BadgeActivator
。
class BadgeValidators
{
private $validators = [];
public function __construct(array $badgeValidators)
{
foreach ($badgeValidators as $badgeValidator) {
$this->addBadgeValidator($badgeValidator);
}
}
public function addBadgeValidator(BadgeValidator $badgeValidator)
{
$this->validators[$badgeValidator->getId()] = $badgeValidator;
}
public function findById($badgeId)
{
if (isset($this->validators[$badgeId])) {
return $this->validators[$badgeId];
}
throw new BadgeValidatorNotFoundException(
"No BadgeValidator found for badge id [$badgeId]"
);
}
}
BadgeValidator
接口是所有具体验证者需要遵守的合同。他们的目的是从$data
映射到Yii的验证器构建或封装附加/唯一验证逻辑。:
interface BadgeValidator
{
public function getId();
public function validate($value = null);
}
具体的Validator将如下所示:
class MaxEnergy35 implements BadgeValidator
{
public function getId()
{
return 'max_nrg_35';
}
public function validate($value = null)
{
$validator = new yii\validators\IntegerValidator();
$validator->min = 35;
return $validator->validate($value['max_energy']);
}
}
您也可以将此课程的配置移至DI配置,例如你可以注入maxEnergy,validatorId和Yii Validator。如果你这样做,你将只有一个类MaxEnergy而不是MaxEnergy35和MaxEnergy100。为了简单起见,我现在就这样保留它。
以下是自定义逻辑:
class DuelRate40 implements BadgeValidator
{
public function getId()
{
return 'duel_rate_40';
}
public function validate()
{
return $this->getSuccessRate(100, $data) <= 40;
}
private function getSuccessRate($limit, array $data)
{
// moved from BadgeActivator
}
}
正如您所看到的,使用自定义逻辑并且不使用Yii验证器来制作BadgeValidators是微不足道的。在这种特殊情况下,我只是将getSuccessRate
rate方法移到了验证器上。您可以很容易地看到它在BadgeActivator上放错位置,因为它没有任何类属性的内聚力。显然,如果您需要在多个验证中使用此代码,您可以再次对其进行配置,因此您只有一个此类验证器,而不是在每个具体验证器中复制该方法。
现在,对于你的switch / case中的每个case
,只需添加另一个具体的BadgeValidator并将其注入DIConfig中的BadgeValidators。
在ced/config/main.php
:
// more config …
components => [
'badgeValidator' => [
'class'=> 'application.components.BadgeValidator',
'badgeValidators' => [
['class'=>'MaxEnergy35'],
['class'=>'MaxEnergy100'],
['class'=>'DuelRate40'],
// more validators …
]
],
// other components …
]
注意:我不知道这是否是使用Yii的DI配置的正确方法。我只是假设您在GitHub上的文件。但我想,如果有必要,你会知道如何改变它。
无论何时需要添加新徽章,只需编写验证器并在配置中将其连接起来。您永远不需要再次触摸BadgeActivator。这也将显着降低BadgeActivator的Cyclomatic Complexity。为上面的每个组件添加单元测试是微不足道的。