169 votes

Quand utiliser des classes statiques ou instanciées

PHP est mon premier langage de programmation. Je n'arrive pas à comprendre quand il faut utiliser des classes statiques ou des objets instanciés.

Je sais que vous pouvez dupliquer et cloner des objets. Cependant, depuis que j'utilise php, tout objet ou fonction se termine toujours par un retour unique (tableau, chaîne de caractères, int) ou par un vide.

Je comprends les concepts dans les livres comme une classe de personnages de jeux vidéo. dupliquer l'objet voiture et rendre le nouveau rouge Tout cela a du sens, mais ce qui n'en a pas, c'est son application dans les applications php et web.

Un exemple simple. Un blog. Quels objets d'un blog seraient mieux implémentés en tant qu'objets statiques ou instanciés ? La classe DB ? Pourquoi ne pas simplement instancier l'objet DB dans la portée globale ? Pourquoi ne pas rendre chaque objet statique à la place ? Qu'en est-il des performances ?

Est-ce que c'est juste du style ? Y a-t-il une façon correcte de faire ce genre de choses ?

123voto

Pascal MARTIN Points 195780

C'est une question très intéressante - et les réponses pourraient l'être aussi ^^.

La façon la plus simple de considérer les choses pourrait être :

  • utiliser une classe instanciée où chaque objet a ses propres données (comme un utilisateur a un nom)
  • utiliser une classe statique lorsqu'il s'agit simplement d'un outil qui fonctionne sur d'autres éléments (comme, par exemple, un convertisseur de syntaxe pour le code BB en HTML ; il n'a pas de vie propre)

(Oui, je l'admets, vraiment très simplifié...)

Une chose à propos des méthodes/classes statiques est qu'elles ne facilitent pas les tests unitaires (au moins en PHP, mais probablement dans d'autres langages aussi).

Une autre caractéristique des données statiques est qu'il n'en existe qu'une seule instance dans votre programme : si vous attribuez à MyClass::$myData une certaine valeur quelque part, elle aura cette valeur, et seulement elle, partout -- En parlant de l'utilisateur, vous ne pourriez avoir qu'un seul utilisateur -- ce qui n'est pas si génial, n'est-ce pas ?

Pour un système de blog, que dire ? Il n'y a pas grand chose que j'écrirais comme statique, en fait, je pense ; peut-être la classe d'accès à la base de données, mais probablement pas, en fin de compte ^^.

69voto

Ionuț G. Stan Points 62482

Les deux principales raisons qui militent contre l'utilisation des méthodes statiques sont les suivantes :

  • code utilisant des méthodes statiques est difficile à test
  • code utilisant des méthodes statiques est difficile à étendre

Avoir un appel de méthode statique à l'intérieur d'une autre méthode est en fait pire que d'importer une variable globale. En PHP, les classes sont des symboles globaux, donc chaque fois que vous appelez une méthode statique, vous vous appuyez sur un symbole global (le nom de la classe). C'est un cas où global est mauvais. J'ai eu des problèmes avec ce genre d'approche avec certains composants de Zend Framework. Il y a des classes qui utilisent des appels de méthodes statiques (factories) afin de construire des objets. Il m'était impossible de fournir une autre fabrique à cette instance afin d'obtenir un objet personnalisé en retour. La solution à ce problème est de n'utiliser que des instances et des méthodes instables et d'imposer des singletons et autres au début du programme.

Miško Hevery qui travaille comme coach agile chez Google, a une théorie intéressante, ou plutôt un conseil, selon lequel nous devrions séparer le temps de création de l'objet du temps d'utilisation de l'objet. Ainsi, le cycle de vie d'un programme est divisé en deux. La première partie (le main() disons), qui s'occupe de tout le câblage des objets dans votre application et de la partie qui effectue le travail réel.

Donc au lieu d'avoir :

class HttpClient
{
    public function request()
    {
        return HttpResponse::build();
    }
}

Nous devrions plutôt le faire :

class HttpClient
{
    private $httpResponseFactory;

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

    public function request()
    {
        return $this->httpResponseFactory->build();
    }
}

Et ensuite, dans la page index/principale, nous ferions (c'est l'étape de câblage des objets, ou le moment de créer le graphe des instances qui seront utilisées par le programme) :

$httpResponseFactory = new HttpResponseFactory;
$httpClient          = new HttpClient($httpResponseFactory);
$httpResponse        = $httpClient->request();

L'idée principale est de découpler les dépendances de vos classes. De cette façon, le code est beaucoup plus extensible et, ce qui est le plus important pour moi, testable. Pourquoi est-il plus important d'être testable ? Parce que je n'écris pas toujours du code de bibliothèque, donc l'extensibilité n'est pas si importante, mais la testabilité est importante quand je fais du refactoring. Quoi qu'il en soit, le code testable donne généralement du code extensible, donc ce n'est pas vraiment une situation de l'un ou l'autre.

Miško Hevery fait également une distinction très nette entre les singletons et les Singletons (avec ou sans S majuscule). La différence est très simple. Les singletons avec un "s" minuscule sont imposés par le câblage dans l'index/main. Vous instanciez un objet d'une classe qui fait no implémentez le modèle Singleton et veillez à ne transmettre cette instance qu'à toute autre instance qui en a besoin. D'autre part, Singleton, avec un "S" majuscule, est une implémentation de l'(anti-)pattern classique. En fait, c'est un global déguisé qui n'a pas beaucoup d'utilité dans le monde PHP. Je n'en ai pas vu jusqu'à présent. Si vous voulez qu'une seule connexion à la base de données soit utilisée par toutes vos classes, il est préférable de faire comme ceci :

$db = new DbConnection;

$users    = new UserCollection($db);
$posts    = new PostCollection($db);
$comments = new CommentsCollection($db);

En faisant ce qui précède, il est clair que nous avons un singleton et que nous avons également un bon moyen d'injecter un mock ou un stub dans nos tests. Il est surprenant de voir comment les tests unitaires conduisent à une meilleure conception. Mais cela prend tout son sens quand on pense que les tests nous obligent à réfléchir à la façon dont nous utiliserions ce code.

/**
 * An example of a test using PHPUnit. The point is to see how easy it is to
 * pass the UserCollection constructor an alternative implementation of
 * DbCollection.
 */
class UserCollection extends PHPUnit_Framework_TestCase
{
    public function testGetAllComments()
    {
        $mockedMethods = array('query');
        $dbMock = $this->getMock('DbConnection', $mockedMethods);
        $dbMock->expects($this->any())
               ->method('query')
               ->will($this->returnValue(array('John', 'George')));

        $userCollection = new UserCollection($dbMock);
        $allUsers       = $userCollection->getAll();

        $this->assertEquals(array('John', 'George'), $allUsers);
    }
}

La seule situation où j'utiliserais (et je les ai utilisés pour imiter l'objet prototype JavaScript en PHP 5.3) des membres statiques est lorsque je sais que le champ respectif aura la même valeur entre les instances. A ce moment là, vous pouvez utiliser une propriété statique et peut-être une paire de méthodes statiques getter/setter. De toute façon, n'oubliez pas d'ajouter la possibilité de surcharger le membre statique avec un membre d'instance. Par exemple, Zend Framework utilisait une propriété statique pour spécifier le nom de la classe d'adaptateur DB utilisée dans les instances de Zend_Db_Table . Cela fait longtemps que je ne les ai pas utilisés, donc il se peut que ce ne soit plus pertinent, mais c'est ainsi que je m'en souviens.

Les méthodes statiques qui ne traitent pas des propriétés statiques devraient être des fonctions. PHP a des fonctions et nous devons les utiliser.

22voto

Rafe Points 2053

Ainsi, en PHP, static peut être appliqué aux fonctions ou aux variables. Les variables non statiques sont liées à une instance spécifique d'une classe. Les méthodes non statiques agissent sur une instance d'une classe. Créons donc une classe appelée BlogPost .

title serait un membre non statique. Il contient le titre de cet article de blog. Nous pourrions également avoir une méthode appelée find_related() . Il n'est pas statique car il requiert des informations d'une instance spécifique de la classe blog post.

Cette classe ressemblerait à quelque chose comme ceci :

class blog_post {
    public $title;
    public $my_dao;

    public function find_related() {
        $this->my_dao->find_all_with_title_words($this->title);
    }
}

D'autre part, en utilisant des fonctions statiques, vous pourriez écrire une classe comme celle-ci :

class blog_post_helper {
    public static function find_related($blog_post) {
         // Do stuff.
    }
}

Dans ce cas, comme la fonction est statique et n'agit pas sur un article de blog particulier, vous devez passer l'article de blog comme argument.

Fondamentalement, il s'agit d'une question sur la conception orientée objet. Vos classes sont les noms de votre système, et les fonctions qui agissent sur elles sont les verbes. Les fonctions statiques sont procédurales. Vous passez l'objet des fonctions comme argument.


Mise à jour : J'ajouterais également que la décision est rarement prise entre les méthodes d'instance et les méthodes statiques, mais plutôt entre l'utilisation de classes et l'utilisation de tableaux associatifs. Par exemple, dans une application de blog, soit vous lisez les articles de blog depuis la base de données et les convertissez en objets, soit vous les laissez dans l'ensemble des résultats et les traitez comme des tableaux associatifs. Ensuite, vous écrivez des fonctions qui prennent des tableaux associatifs ou des listes de tableaux associatifs comme arguments.

Dans le scénario OO, vous écrivez des méthodes sur votre site Web. BlogPost qui agissent sur des messages individuels, et vous écrivez des méthodes statiques qui agissent sur des collections de messages.

14voto

troelskn Points 51966

Est-ce que c'est juste du style ?

Un long chemin, oui. Vous pouvez écrire de très bons programmes orientés objet sans jamais utiliser de membres statiques. En fait, certains diront que les membres statiques sont une impureté en premier lieu. Je suggérerais qu'en tant que débutant en OOP, vous essayiez d'éviter les membres statiques. Cela vous obligera à écrire dans un langage de programmation orienté objet. orienté objet plutôt que procédure le style.

10voto

grantwparks Points 716

"Avoir un appel de méthode statique à l'intérieur d'une autre méthode est en fait pire que d'importer une variable globale." (définir "pire")... et "Les méthodes statiques qui ne traitent pas de propriétés statiques devraient être des fonctions".

Ce sont deux déclarations assez radicales. Si j'ai un ensemble de fonctions qui sont liées par leur sujet, mais que les données d'instance sont totalement inappropriées, je préférerais qu'elles soient définies dans une classe et non dans l'espace de noms global. J'utilise simplement la mécanique disponible en PHP5 pour

  • leur donner à tous un espace de nom -- en évitant tout conflit de noms
  • les garder physiquement ensemble au lieu de les disperser dans un projet - les autres développeurs peuvent plus facilement trouver ce qui est déjà disponible et sont moins susceptibles de réinventer la roue
  • laissez-moi utiliser les constantes de classe au lieu des définitions globales pour toute valeur magique

c'est juste un moyen pratique d'imposer une plus grande cohésion et un couplage plus faible.

Et pour info, il n'existe pas, du moins en PHP5, de "classes statiques" ; les méthodes et les propriétés peuvent être statiques. Pour empêcher l'instanciation de la classe, on peut aussi la déclarer abstraite.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X