La délégation de méthodes

La délégation est un principe de programmation selon lequel un objet, au lieu d'accomplir une méthode qu'il définit, la délègue à un objet auxiliaire. On pourrait dire qu'il lui refile le bébé, se contentant de passer les arguments et de retourner le résultat. La délégation est un des principes fondamentaux qui sous-tendent d'autres systèmes d'abstraction comme la composition ou les mixins.

Afin d'illustrer ce principe de délégation, je vous propose de découvrir son implémentation dans la classe WdModel, la classes de base des modèles de données du framework WdCore. Nous verrons les étapes qui ont conduit à l'implémentation actuelle : depuis le passage d'arguments jusqu'à l'utilisation de la trace d'exécution.

La délégation à l'œuvre dans les modèles de données

Le principe de délégation est à l'œuvre pour les méthodes select, where, order, exists et all des modèles de données du framework WdCore. Chacune de ses méthodes crée un objet WdActiveRecordQuery, lui applique la méthode invoquée en lui passant les arguments, puis retourne l'objet pour que le développeur puisse affiner sa requête ou l'exécuter.

Passer les arguments

Dans leur première version, chaque méthode créait l'objet délégué et lui passait ses arguments. Voici par exemple la première implémentation de la méthode where :

<?php

class WdModel extends WdDatabaseTable
{
    ...
    public function where($conditions$conditions_args=null)
    {
        $args = func_get_args();
        $arq = new WdActiveRecordQuery($this);

        return call_user_func_array(array($arq, __FUNCTION__)$args);
    }
}

Si l'on met de côté la déclaration des paramètres de la fonction, on s'aperçoit que le code de délégation est vraiment passe partout, et qu'il s'applique de la même façon pour chaque méthode, nous pouvons donc le mutualiser.

Mutualisation du code de délégation

La mutualisation du code de délégation est assez simple. La méthode defer_to_actionrecord_query() crée l'objet « délégué » et invoque la méthode spécifiée avec les arguments passés depuis la méthode d'appel.

<?php

class WdModel extends WdDatabaseTable
{
    ...
    private function defer_to_actionrecord_query($function$args)
    {
        $arq = new WdActiveRecordQuery($this);

        return call_user_func_array(array($arq$function)$args);
    }

    public function where($conditions$conditions_args=null)
    {
        $args = func_get_args();

        return $this->defer_to_actionrecord_query(__FUNCTION__, $args);
    }
}

On peut encore simplifier le code en utilisant la trace d'exécution.

Utiliser la trace d'exécution

Le nom de la méthode qui appelle defer_to_actionrecord_query() ainsi que ses arguments peuvent être obtenus depuis la trace d'exécution, le code devient alors très simple :

<?php

class WdModel extends WdDatabaseTable
{
    ...
    private function defer_to_actionrecord_query()
    {
        $trace = debug_backtrace(false);
        $arq = new WdActiveRecordQuery($this);

        return call_user_func_array(array($arq$trace[1]['function'])$trace[1]['args']);
    }

    public function joins($expression)
    {
        return $this->defer_to_actionrecord_query();
    }

    public function select($expression)
    {
        return $this->defer_to_actionrecord_query();
    }

    public function where($conditions$conditions_args=null)
    {
        return $this->defer_to_actionrecord_query();
    }

    public function order($order)
    {
        return $this->defer_to_actionrecord_query();
    }
}

Vous l'avez surement compris le but de cet article était également de présenter une utilisation pratique de la trace d'exécution, pour autre chose que du debug. En écrivant ces lignes je me suis pris à rêver à une variable magique $_TRACE qui permettrait de consulter plus simplement la trace d'exécution en oubliant quelques méthodes :

  • debug_backtrace >> $_TRACE
  • func_get_args() >> $_TRACE[0]['args']
  • func_get_arg(1) >> $_TRACE[0]['args'][1]
  • func_num_args() >> count($_TRACE[0]['args'])

Conclusion

Le principe de délégation est assez simple à mettre en place et permet d'étendre les fonctionnalités d'un objet de façon transparente. Si vous souhaitez en savoir plus sur la délégation je vous invite à consulter les articles Delegation pattern et Delegation (programming) sur Wikipedia.

Dans un prochain article nous verrons ensemble les fonctionnalités de la classe WdObject qui permet de faire du Mixin en PHP d'une façon très simple.

Laisser un commentaire

Pas de commentaire