一组函数的替代?

时间:2014-02-11 01:45:02

标签: php arrays function functional-programming

我正在编写一个应用程序(php),它需要很长的类似但不同的函数列表,这些函数由一组键调用

$functions = [
    "do this" => function() {
        // does this
    },
    "do that" => function() {
        // does that
    }
] 
etc.

我选择将相似的函数放在一个数组中,因为它们不相似足够 - 使用一个充满条件语句的大函数得到相同的结果是行不通的。并且我确实需要只能通过密钥来调用它们,例如:

$program = ["do this", "do that", "do this"];
foreach ($program as $k => $v) {
    $functions[$v]();
}

事情是这个函数 - 数组结构引起了很多问题,例如我很难从另一个数组函数中调用一个数组函数,例如这不起作用:

"do that" => function() {
    $functions["do this"]();
}

也不是:

"do that" => function() {
    global $functions;
    $functions["do this"]();
}

或者这个:

"do that" => function($functions) {
    $functions["do this"]();
}

$functions["do that"]($functions);

我想我可以使用一个很长的开关语句

function similar_functions($key) {
    switch ($key) {
        case "do this":
            // does this
        break;
        case "do that":
            // does that
        break;
    }
}

但那似乎并不是一种好的做法。或许它是?

那么,我的替代方案是什么?我应该选择开关结构吗?还是有另一种更好的解决方案吗?

6 个答案:

答案 0 :(得分:21)

在php中,闭包对于性能和内存耗资来说是昂贵的。程序编码引发了泥球,意大利面条代码和其他反模式的大球。切换结构非常难以测试,这违反了OCP

您应该以SOLID方式优先选择OOP,以避免冗余,提高可扩展性和可维护性。这是提供一组可重复使用的功能的最佳实践。 您还可以在层和模块中分离代码,以降低复杂性并提高可互换性。

在您的情况下,您的类可以实现__invoke将其称为invokable,并且您的键可以是这些类的完全限定名称空间,因此您可以像函数一样调用它。 从现在开始,您还可以使用继承,多态或复合,装饰等设计模式来重用其他函数或添加函数。

这是一个简单的例子..

<?php
use Foo\Bar;

class This
{
    public function __invoke()
    {
        $this->execute();
    }

    public function execute()
    {
        /* ... */
    }
 }

class That extends This
{
    public function execute()
    {
        /* ... */
    }
 }

$namespaces = array("\Foo\Bar\This", "\Foo\Bar\That"); 
foreach ($namespaces as $fullQualifiedNamespace) {
    /** @var callable $fullQualifiedNamespace */
    $fullQualifiedNamespace(); 
}

通过实现This和That的特定接口也可以访问此行为。 在迭代中,您可以检查接口以调用已定义的合同。或者您构建一个Process Class,您可以在其中添加实现此接口的对象。 Process类可以执行所有附加对象(Chain of Responsibility)。

我更喜欢责任链模式这是大多数开发人员可以理解的,而不是像PHP的__invoke拦截器那样 magic 。在工厂中,您可以定义链,并且可以定义链或链接对象的其他依赖关系。

答案 1 :(得分:7)

您可以以更干净的方式执行此操作,即将所有这些功能封装到类中。它们可以是静态的也可以是动态的,在这种情况下我猜的并不重要。我将展示两个例子......

<强> 1。动态方法

class MyFunctions
{

    public function doThis()
    {
        /* implement do this here */
    }

    public function doThat()
    {
        /* implement do that here */
    }
    /* ... */
}

/* then somewhere else */
$program = ["doThis", "doThat", "doThis"];
$myFunctions = new MyFunctions;
foreach ($program as $method) {
    $myFunctions->$method();
}

<强> 2。静态方法

class MyFunctions
{

    public static function doThis()
    {
        /* implement do this here */
    }

    public static function doThat()
    {
        /* implement do that here */
    }
    /* ... */
}

/* then somewhere else */
$program = ["doThis", "doThat", "doThis"];
foreach ($program as $method) {
    MyFunctions::$method();
}

恕我直言,这比在闭包数组中实现这些函数要简洁......而且比实现switch更好,因为你仍然需要在循环中调用它 - 所以为什么要放慢另一个{{1} }?

答案 2 :(得分:3)

我全都赞成Mamuzhis answer所描述的内容,但我确实希望为您提供“快速解决方案”:

$functions = [];

$functions['do this'] = function () {
    // does this
};

$functions['do call'] = function () use ($functions) {
    // calls 'do this'
    $functions['do this']();
}

$functions['do manipulation'] = function () use (&$functions) {
    // manipulates $functions
    $functions['do something else'] = function () {
        // does something else
    };
}

我认为这是非常自我解释的。

答案 3 :(得分:2)

旧的命令模式怎么样? ;)

<?php

interface Command{
    public function execute();
}

class DoThatCommand implements Command{
    public function execute()
    {
        echo "doing that...";
    }
}

class DoThisCommand implements Command{
    public function execute(){
        echo "doing this...";
        $doThatCommand = new DoThatCommand();
        $doThatCommand->execute();

    }
}

$program = [new DoThatCommand(), new DoThisCommand(), new DoThatCommand()];

foreach ($program as $command) {
    $command->execute();
    echo "\n";
}
?>

或以更优雅的依赖注射方式,您可以这样做:

<?php

interface Command{
    public function execute();
}

class DoThatCommand implements Command{
    public function execute()
    {
        echo "doing that... \n";
    }
}

class DoThisCommand implements Command{
    public function execute(){
        echo "doing this... \n";
    }
}

class DoTheThirdThingCommandThatDependsOnDoThisCommand implements Command{

    public $doThisCommand;

    public function __construct(DoThisCommand $doThisCommand)
    {
        $this->doThisCommand = $doThisCommand; 
    }

    public function execute()
    {
        echo "doing the ThirdThingCommand that depends on DoThisCommand... \n";
        $this->doThisCommand->execute();
    }
}

$command1 = new DoThatCommand(); 
$command2 = new DoThisCommand();
$command3 = new DoTheThirdThingCommandThatDependsOnDoThisCommand($command2);


$program = [$command1, $command2, $command3];
echo "<pre>";
foreach ($program as $command) {
    $command->execute();
}
?>

答案 4 :(得分:2)

可能是这样的:

/**
 * Save an array with function friendly names and its associated function and params
 */
$functions = array(
    'Do This' => array(
        'fn_name' => 'fn1',
        'fn_args' => array('fn1', 'from fn1')),
    'Do That' => array(
        'fn_name' => 'fn2'
    )
);

/**
 * Call this per each value of the functions array, passing along that function data as parameter
 */
function execute_fn($fn_data) {
    $fn_name = $fn_data['fn_name'];

    if(isset($fn_data['fn_args'])) {
        call_user_func_array($fn_name, $fn_data['fn_args']);
    } else {
        call_user_func($fn_name);
    }


}

function fn1($val1, $val2) {
    echo "I'm being called from $val1 and my second argument value is $val2 \n";
}

function fn2() {
    echo "Doing something for fn2 \n";
    fn1('fn2', 'from fn2');
}

/**
 * Call this to execute the whole set of functions in your functions array
 */
foreach ($functions as $key => $value) {
    execute_fn($value);
}

这样你就可以在$functions中定义一个带有函数名和参数值的数组,如果那个特定函数需要的话。

答案 5 :(得分:1)

使用长switch语句的巨型函数更好,因为它允许您将similar_functions("do this")作为similar_functions("do that")的一部分调用:

function similar_functions($key) {
    switch ($key) {
        case "do this":
            // does this
        break;
        case "do that":
            similar_functions("do this");
            // and then does that
        break;
    }
}