Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An alternative to an array of functions?

I'm programming an app (php) which requires a very long list of similar yet different functions, which are being called by a set of keys:

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

I've chosen to place the similar functions in an array because they are not similar enough - getting the same result with one big function which is full of conditional statements isn't gonna work. And I do need to be able to call them only by key, for example:

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

Thing is this functions-array structure is causing many problems, for example I'm having a hard time calling one array function from within another array function, e.g. this doesn't work:

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

nor this:

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

or this:

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

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

I guess I could have one giant function with a long switch statement:

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

But that doens't really seem like good practice. Or maybe it is?

So, what are my alternatives? Should I go with the switch structure? Or is there another, better solution?

like image 586
Roy Avatar asked Feb 11 '14 01:02

Roy


1 Answers

Closures are expensive for performance and memoryusage in php. Procedural coding provoked a big ball of mud, spaghetti code and other anti patterns. Switch structures are very hard to test and it's a violation of OCP.

You should prefer OOP in SOLID way to avoid redundancy, improving scalability and maintainablity. That is the best practice to provide a set of functions which are reuseable. You can also seperate your code in layers and modules to reduce complexity and improve interchangability.

In your case your classes can implement __invoke to call it as invokable and your keys could be the fullqualified namespaces for these classes, so you can call it like a function. From now on you can also use inheritence, polymorphism or design patterns like composite, decorator etc. to reuse other functions or to add functions.

Here a simple Example..

<?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 behavior is also reachable by implementing a specific interface to This and That. Within iteration you can check the interface to call the defined contract. Or you build a Process Class where you can add objects which implements this interface. The Process class can execute all attached objects (Chain of Responsibility).

I would prefer the Chain of Responsibility Pattern this is understandable by most developers and not so magic like PHP's __invoke interceptor. In a factory you can define your chain and you are able to define other dependencies to the chain or to the attached chain objects.

like image 162
Mamuz Avatar answered Oct 16 '22 19:10

Mamuz