使用Case而不是许多小函数的大函数

时间:2015-10-13 18:39:38

标签: php oop

将代码分解为只执行一个且只有一个简单功能的非常小的组件的好处是显而易见的。但是没有什么要求我们应该让每个功能本身成为一个独立的功能。

考虑以下情况:

  • 有一个很大的功能,但是函数中包含的每个案例都是隔离的,并且只能自行运行。所有案例块都可以只是复制/粘贴到不同的代码体中,并包含在它们的函数名中。所以它的模块化。 永远不会有任何多个案例(ifs)

  • 驻留在案例块中的所有小函数都使用$ vars数组作为其主变量。因此,任何格式的任何数量的变量都可以作为数组的一部分传递给父迭代器。没有限制。

  • 父迭代器可以通过请求特定操作从任何地方,甚至在其自身内的任何地方运行。即$ this-> run_actions('close_ticket');

  • 它在需要运行的常用程序方面具有巨大优势,可能需要在所有请求的操作上运行。输出缓冲是一个,任何动作都可以挂钩,过滤或任何其他可以想象的全系统。

  • 此格式允许任何未来需要在任何操作之前和之后运行的新程序和/或任何操作的输入和输出,以便轻松集成。 (对于我手中的特殊情况,将来这种情况的出现是肯定的!。)如果所有这些行为都被分成小功能,你需要去和更改每个函数的钩子和过滤器,或者仍然有某种其他函数将这些函数分配到这些小函数上。使用此格式,您只需将它们放在开关/案例块之前或之后。

  • 代码阅读很简单:当您看到特定案例时,您知道它的作用 - > 'view_tickets'是故障单视图操作,它需要$ vars作为输入。

显然,真正的超大质量功能会有各种缺点。

所以,问题是:

假设函数的大小保持在合理的大小,并且保留了模块化的原则和每个案例的一个简单操作,同时考虑到将来使用此代码的任何人都不需要考虑这段代码并且不能修改这段代码,只需要知道钩子和过滤器,这些钩子和过滤器将在代码本身以外的地方进行记录,(包括他们使用的任何变量)你认为这可能是一种有效的方法来组合需要运行的常用程序的任务在他们身上?

public function run_actions($action,$vars=false)
{

    // Global, common filters and procedures which all actions need or may need

    ob_start();

    switch($action)
    {

        case 'view_tickets':
        {
            // Do things
            // Echo things if necessary

            break;
        }
        case 'close_ticket':
        {
            // Do things
            // Echo things if necessary

            break;
        }
        case 'do_even_more_stuff':
        {
            // Do things
            // Echo things if necessary

            break;
        }
        // Many more cases as needed
    }

    // Even more common post-processing actions, filters and other stuff any action may need

    $output=ob_get_clean();

    return $output;
}

4 个答案:

答案 0 :(得分:2)

您可以使用多态替换条件。使用" execute"等方法创建一个抽象动作类。然后为实现该方法的所有各种操作创建子类。

e.g。

function run_actions(IAction action) {
    //...
    action->execute();
    //...
}

这样,如果您需要引入其他行为,您将无需修改和测试具有多种职责的长期运行。

答案 1 :(得分:1)

各种缺点:

切换案例都使用$ vars,因此他们没有特定的签名。

  • 这隐藏了开发人员的签名,因此被迫阅读代码。
  • 你不能对$ vars进行类型提示(强制参数为数组,某些类的实例等)
  • 没有IDE自动完成

更容易犯错误

  • 忘了break,你就完成了。没有可识别的错误。

难以重构

  • 如果需要将代码提取到函数中,您会怎么做?您需要复制预处理(ob_start等)或更改所有内容
  • 如果你需要在没有预处理的情况下运行动作,你会怎么做?

我同意这很简单,但它有长期的缺点。由你来达到正确的平衡:)

答案 2 :(得分:1)

当我看到这种架构时,我认为它开始在现有架构之上构建一种新的编程语言。如果您正在构建的功能比您构建它们的语言更好,那么这并不总是坏事,但是值得挑战的是这些功能。

在这种情况下,您重新发明的语言部分是函数调度:您有一个命名操作,它接受任意参数,并运行任意代码。 PHP已经做到了这一点,非常有效;它还具有您的系统缺少的功能,例如内置的数量和(在某种程度上)参数类型的检查。此外,通过发明非标准的“语法”,现有的工具也不会起作用 - 例如,他们不会将这些行为识别为自我文档结构。

您获得的主要部分是能够围绕操作添加预处理和后处理。如果没有其他方法可以实现这一点,那么权衡可能是值得的,但幸运的是,你有更好的选择,例如:将每个动作放入一个函数中,并将其作为回调传递给包装函数;或者使每个动作成为一个对象,并使用继承或组合来附加前处理和后处理而不重复它。

通过将参数包装在数组中,您还可以模拟PHP缺少的命名参数。如果函数需要许多参数,这可能是一个好主意,其中一些参数可能是可选的,但它确实带来了重新发明语言通常会为您执行的处理的缺点,例如应用正确的默认值,抱怨缺少必需的项目等等

答案 3 :(得分:0)

有一个简单的原则,即不要使用超过2个标签缩进。

例如:

public function applyRules($rules){
    if($rules){
        foreach($rules as $rule){
            //apply ryle
        }
    }
}

重构时会变得更好:

public function applyRules($rules){
    if($rules){
        $this->executeRules($rules);
    }
}

private function executeRules($rules){
    foreach($rules as $rule){
        $rule->execute();
    }
}

通过这种方式,您的代码将得到更好的重构,您可以应用更多的单元测试。

另一条规则说不要使用else,而是破坏代码,例如:

public function usernameExists($username){
    if($username){
        return true;
    }else{
        return false;   
    }
}

而不是这样做,你应该这样做:

public function usernameExists($username){
    if($username){
        return true;
    }
    return false;
}

只有这样才能确保您的代码100%