web-dev-qa-db-fra.com

Quand utiliser les classes statiques vs instanciées

PHP est mon premier langage de programmation. Je ne sais pas trop quand utiliser des classes statiques par rapport à des objets instanciés.

Je me rends compte que vous pouvez dupliquer et cloner des objets. Cependant, tout au long de mon temps d'utilisation de php, chaque objet ou fonction finissait toujours par une valeur de retour unique (array, string, int) ou void.

Je comprends des concepts de livres comme une classe de personnages de jeux vidéo. dupliquer un objet voiture et faire en sorte que le nouvel objet soit rouge, tout cela a du sens, mais ce n’est pas 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 de base de données dans la portée globale? Pourquoi ne pas rendre chaque objet statique à la place? Qu'en est-il de la performance?

Est-ce juste du style? Y a-t-il une bonne façon de faire ce genre de choses?

166
user73119

C'est une question assez intéressante - et les réponses pourraient l'être aussi ^^

Le moyen le 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)
  • utilisez 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 un code BB en HTML; il n'a pas de vie à lui seul)

(Ouais, je l'avoue, vraiment trop simplifié ...)

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

Une autre chose à propos des données statiques est qu’il n’existe qu’une seule instance dans votre programme: si vous définissez MyClass :: $ myData quelque part, il aura cette valeur, et seulement elle, partout où - Parlant de l’utilisateur, vous seriez en mesure d'avoir un seul utilisateur - ce qui n'est pas si génial, n'est-ce pas?

Que pourrais-je dire pour un système de blog? 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, à la fin ^^

120
Pascal MARTIN

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

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

Avoir un appel de méthode statique dans une autre méthode est en réalité pire que d'importer une variable globale. En PHP, les classes sont des symboles globaux. Ainsi, chaque fois que vous appelez une méthode statique, vous vous fiez à un symbole global (le nom de la classe). C'est un cas où le global est le mal. J'ai eu des problèmes avec ce type d'approche avec certains composants de Zend Framework. Il existe des classes qui utilisent des appels de méthodes statiques (fabriques) afin de construire des objets. Il était impossible pour moi de fournir une autre usine à cette instance afin de récupérer un objet personnalisé. La solution à ce problème consiste à n'utiliser que des instances et des méthodes d'installation et à appliquer des singletons et autres au début du programme.

Miško Hevery , qui travaille en tant que coach agile chez Google, a une théorie intéressante, ou plutôt, conseille de séparer l'heure de création de l'objet de son utilisation. Donc, le cycle de vie d'un programme est divisé en deux. La première partie (la méthode main(), disons), s'occupe de tout le câblage des objets dans votre application et de la partie effectuant le travail proprement dit.

Donc au lieu d'avoir:

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

Nous devrions plutôt faire:

class HttpClient
{
    private $httpResponseFactory;

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

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

Et ensuite, dans l'index/la page principale, nous ferions (c'est l'étape de câblage d'objet, ou le temps de créer le graphique d'instances à utiliser par le programme):

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

L'idée principale est de dissocier les dépendances de vos classes. De cette façon, le code est beaucoup plus extensible et, pour moi, la partie la plus importante, testable. Pourquoi est-il plus important d'être testable? Comme je n'écris pas toujours le code de la bibliothèque, l'extensibilité n'est pas si importante que la testabilité est importante lorsque je refacture. Quoi qu'il en soit, le code testable produit généralement un code extensible, ce qui ne signifie pas vraiment qu'il en va de même.

Miško Hevery établit également une distinction claire entre singletons et singletons (avec ou sans majuscule). La différence est très simple. Les singletons avec un "s" minuscule sont appliqués par le câblage dans l'index/main. Vous instanciez un objet d'une classe qui pas implémente le modèle Singleton et veillez à ne transmettre cette instance qu'à toute autre instance qui en a besoin. De son côté, Singleton, avec un "S" majuscule, est une implémentation du modèle (anti) classique. Fondamentalement un déguisement global qui n’a pas beaucoup d’utilisation dans le monde PHP. Je n’en ai pas vu jusqu’à présent. Si vous souhaitez qu'une seule connexion à une base de données soit utilisée par toutes vos classes est préférable de le faire comme ça:

$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 moyen agréable d’injecter une maquette ou un bout dans nos tests. C'est étonnamment comment les tests unitaires conduisent à une meilleure conception. Mais cela a beaucoup de sens quand vous pensez que les tests vous obligent à penser à la façon dont vous utiliseriez 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 dans PHP 5.3) membres statiques est lorsque je sais que le champ respectif aura la même valeur cross À ce stade, vous pouvez utiliser une propriété statique et peut-être une paire de méthodes getter/setter statiques. Quoi qu'il en soit, n'oubliez pas d'ajouter la possibilité de remplacer le membre statique par un membre d'instance. Par exemple, Zend Framework utilisait une méthode statique. propriété afin de spécifier le nom de la classe d’adaptateur de base de données utilisée dans les instances de Zend_Db_Table. Cela fait longtemps que je ne les utilise pas, ce n'est peut-être plus pertinent, mais c'est comme ça que je m'en souviens.

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

68
Ionuț G. Stan

Ainsi, in PHP statique peut être appliqué à des fonctions ou à des 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. une classe appelée BlogPost.

title serait un membre non statique. Il contient le titre de ce blog. Nous pourrions aussi avoir une méthode appelée find_related(). Ce n'est pas statique car il nécessite des informations provenant d'une instance spécifique de la classe de publication de blog.

Cette classe ressemblerait à quelque chose comme ça:

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, la fonction étant statique et n'agissant pas sur un article de blog particulier, vous devez transmettre l'article en tant qu'argument.

Fondamentalement, il s'agit d'une question de conception orientée objet. Vos classes sont les noms dans votre système, et les fonctions qui agissent sur elles sont les verbes. Les fonctions statiques sont procédurales. Vous transmettez l'objet des fonctions en tant qu'arguments.


Mise à jour: j'ajouterais également que la décision est rarement prise entre les méthodes d'instance et les méthodes statiques, et davantage entre l'utilisation de classes et l'utilisation de tableaux associatifs. Par exemple, dans une application de blogging, vous pouvez soit lire les articles de blog de la base de données et les convertir en objets, soit les laisser dans le jeu de résultats et les traiter comme des tableaux associatifs. Ensuite, vous écrivez des fonctions qui prennent comme arguments des tableaux associatifs ou des listes de tableaux associatifs.

Dans le scénario OO, vous écrivez des méthodes sur votre classe BlogPost qui agissent sur des publications individuelles, et des méthodes statiques agissant sur des collections de publications.

22
Rafe

Est-ce juste du style?

Un long chemin, oui. Vous pouvez écrire parfaitement de bons programmes orientés objet sans jamais utiliser de membres statiques. En fait, certaines personnes diraient que les membres statiques sont une impureté en premier lieu. Je suggérerais qu'en tant que débutant en programmation, vous essayez d'éviter les membres statiques tous ensemble. Cela vous obligera à écrire dans un style orienté objet plutôt que procédural.

14
troelskn

J'ai une approche différente de la plupart des réponses ici, en particulier avec PHP. Je pense que toutes les classes devraient être statiques à moins que vous n'ayez une bonne raison de ne pas le faire. Certaines des raisons "pourquoi pas" sont:

  • Vous avez besoin de plusieurs instances de la classe
  • Votre cours doit être prolongé
  • Certaines parties de votre code ne peuvent pas partager les variables de classe avec d'autres parties.

Laissez-moi prendre un exemple. Étant donné que chaque script PHP génère du code HTML, mon framework comporte une classe d'écriture html. Cela garantit qu'aucune autre classe ne tentera d'écrire HTML, car il s'agit d'une tâche spécialisée qui doit être concentrée dans une seule classe. .

Typiquement, vous utiliseriez la classe html comme ceci:

html::set_attribute('class','myclass');
html::tag('div');
$str=html::get_buffer();

Chaque fois que get_buffer () est appelé, il réinitialise tout afin que la classe suivante à utiliser l'écrivain HTML démarre dans un état connu.

Toutes mes classes statiques ont une fonction init () qui doit être appelée avant que la classe ne soit utilisée pour la première fois. C’est plus par convention qu’une nécessité.

L'alternative à une classe statique dans ce cas est compliquée. Vous ne voudriez pas que toutes les classes ayant besoin d'écrire un tout petit peu de HTML aient à gérer une instance de l'écrivain HTML.

Maintenant, je vais vous donner un exemple de quand ne pas utiliser de classes statiques. Ma classe de formulaire gère une liste d'éléments de formulaire, tels que des entrées de texte, des listes déroulantes, etc. Il est généralement utilisé comme ceci:

$form = new form(stuff here);
$form->add(new text(stuff here));
$form->add(new submit(stuff here));
$form->render(); // Creates the entire form using the html class

Il est impossible de faire cela avec des classes statiques, d'autant plus que certains des constructeurs de chaque classe ajoutée effectuent beaucoup de travail. En outre, la chaîne d'héritage pour tous les éléments est assez complexe. Il s'agit donc d'un exemple clair dans lequel les classes statiques ne doivent pas être utilisées.

La plupart des classes d'utilitaires telles que celles convertissant/formatant des chaînes sont de bonnes candidates pour devenir une classe statique. Ma règle est simple: tout est statique dans PHP à moins qu'il n'y ait une raison pour laquelle cela ne devrait pas l'être.

12
JG Estiot

"Avoir un appel de méthode statique dans une autre méthode est en réalité pire que d'importer une variable globale." (définissez "pire") ... et "Les méthodes statiques qui ne traitent pas les propriétés statiques doivent être des fonctions".

Ce sont deux déclarations assez radicales. Si j'ai un ensemble de fonctions qui sont liées dans le 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 les mécanismes disponibles dans PHP5 pour

  • donnez-leur tous un espace de noms - en évitant les conflits de noms
  • conservez-les physiquement ensemble au lieu de vous 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
  • permettez-moi d'utiliser des consts de classe au lieu de définitions globales pour toutes les valeurs magiques

c'est tout simplement un moyen pratique de renforcer la cohésion et de réduire le couplage.

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

10
grantwparks

Tout d'abord, demandez-vous ce que cet objet va représenter. Une instance d'objet est utile pour opérer sur des ensembles distincts de données dynamiques.

Un bon exemple serait ORM ou la couche d'abstraction de base de données. Vous pouvez avoir plusieurs connexions de base de données.

$db1 = new Db(array('Host' => $Host1, 'username' => $username1, 'password' => $password1));
$db2 = new Db(array('Host' => $Host2, 'username' => $username2, 'password' => $password2));

Ces deux connexions peuvent désormais fonctionner indépendamment:

$someRecordsFromDb1 = $db1->getRows($selectStatement);
$someRecordsFromDb2 = $db2->getRows($selectStatement);

Désormais, au sein de ce paquet/bibliothèque, il peut y avoir d'autres classes telles que Db_Row, etc. pour représenter une ligne spécifique renvoyée par une instruction SELECT. Si cette classe Db_Row était une classe statique, cela supposerait que vous n'ayez qu'une seule ligne de données dans une base de données et qu'il serait impossible de faire ce qu'une instance d'objet pourrait faire. Avec une instance, vous pouvez maintenant avoir un nombre illimité de lignes dans un nombre illimité de tables dans un nombre illimité de bases de données. La seule limite est le matériel du serveur;).

Par exemple, si la méthode getRows sur l'objet Db renvoie un tableau d'objets Db_Row, vous pouvez désormais agir sur chaque ligne indépendamment l'une de l'autre:

foreach ($someRecordsFromDb1 as $row) {
    // change some values
    $row->someFieldValue = 'I am the value for someFieldValue';
    $row->anotherDbField = 1;

    // now save that record/row
    $row->save();
}

foreach ($someRecordsFromDb2 as $row) {
    // delete a row
    $row->delete();
}

Un bon exemple de classe statique serait quelque chose qui gère les variables de registre, ou les variables de session, car il n'y aura qu'un registre ou une session par utilisateur.

Dans une partie de votre application:

Session::set('someVar', 'toThisValue');

Et dans une autre partie:

Session::get('someVar'); // returns 'toThisValue'

Étant donné qu'il ne peut y avoir qu'un seul utilisateur à la fois par session, il est inutile de créer une instance pour la session.

J'espère que cela aide, avec les autres réponses, à clarifier les choses. Notez également " cohésion " et " couplage ". Ils décrivent de très très bonnes pratiques à utiliser lors de la rédaction de votre code, qui s’appliquent à tous les langages de programmation.

7
Tres

Si votre classe est statique, cela signifie que vous ne pouvez pas transmettre son objet à d'autres classes (car il n'y a pas d'instance possible), ce qui signifie que toutes vos classes utiliseront directement cette classe statique, ce qui signifie que votre code est maintenant étroitement couplé à la classe. .

Le couplage étroit rend votre code moins réutilisable, fragile et sujet aux bogues. Vous voulez éviter aux classes statiques de pouvoir passer instance de la classe à d'autres classes.

Et oui, ce n’est que l’une des nombreuses autres raisons, dont certaines ont déjà été mentionnées.

6

J'aimerais dire qu'il y a définitivement un cas où je voudrais des variables statiques dans des applications multilingues. Vous pouvez avoir une classe à laquelle vous transmettez une langue (par exemple, $ _SESSION ['langue']) et accéder à son tour à d'autres classes conçues de la manière suivante:

Srings.php //The main class to access
StringsENUS.php  //English/US 
StringsESAR.php  //Spanish/Argentina
//...etc

Utiliser Strings :: getString ("somestring") est un bon moyen d’abstraire l’utilisation de votre langue dans votre application. Vous pouvez le faire comme bon vous semble, mais dans ce cas, le fait que chaque fichier de chaînes ait des constantes avec des valeurs de chaîne auxquelles accède la classe Strings fonctionne plutôt bien.

3
dudewad

En général, vous devez utiliser des variables et des fonctions de membre, sauf si cela doit absolument être partagé entre toutes les instances ou si vous créez un singleton. L'utilisation de données membres et de fonctions membres vous permet de réutiliser vos fonctions pour plusieurs éléments de données différents. Vous ne pouvez disposer que d'une copie des données sur laquelle vous opérez si vous utilisez des données et des fonctions statiques. De plus, bien que cela ne s'applique pas à PHP, les fonctions et données statiques font que le code n'est pas réentrant, alors que les données de classe facilitent la réentrance.

3