Quand utiliser des classes statiques ou instanciées

170

PHP est mon premier langage de programmation. Je ne peux pas vraiment comprendre quand utiliser des classes statiques ou des objets instanciés.

Je réalise que vous pouvez dupliquer et cloner des objets. Cependant, pendant tout mon temps en utilisant php, tout objet ou fonction se terminait toujours par une valeur de retour unique (tableau, chaîne, int) ou void.

Je comprends les concepts des livres comme une classe de personnages de jeux vidéo. dupliquez l'objet voiture et rendez le nouveau rouge , cela a du sens, mais ce qui ne l'est 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? Et les performances?

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

utilisateur73119
la source

Réponses:

123

C'est une question assez intéressante - et les réponses peuvent aussi devenir intéressantes ^^

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 quand c'est juste un outil qui fonctionne sur d'autres choses (comme, par exemple, un convertisseur de syntaxe pour le code BB en HTML; il n'a pas de vie en soi)

(Ouais, j'avoue, vraiment vraiment trop 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 chose à propos des données statiques est qu'il n'en existe qu'une seule instance dans votre programme: si vous définissez MyClass :: $ myData sur une 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 génial, n'est-ce pas?

Pour un système de blog, que pourrais-je 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, à la fin ^^

Pascal MARTIN
la source
Donc, fondamentalement, utilisez des objets lorsque vous travaillez avec des choses uniques comme une photo, un utilisateur, un message, etc. et utilisez statique lorsque cela est destiné à des choses générales?
Robert Rocha
1
bien en parlant de l'utilisateur, vous n'avez qu'un seul utilisateur actif à chaque demande. donc haing l'utilisateur actif de la requête statique aurait du sens
My1
69

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

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

Avoir un appel de méthode statique dans 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 fiez à un symbole global (le nom de la classe). C'est un cas où le global est mauvais. 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 (usines) pour créer 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 instace 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, que nous devrions séparer le temps de création de l'objet du moment où nous utilisons l'objet. Le cycle de vie d'un programme est donc divisé en deux. La première partie ( main()disons la méthode), qui prend en charge tout le câblage des objets dans votre application et la partie qui fait le travail réel.

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 puis, dans l'index / page principale, nous ferions (c'est l'étape de câblage de l'objet, ou le temps de créer le graphique des instances à utiliser 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, la partie la plus importante pour moi, testable. Pourquoi est-il plus important d'être testable? Parce que je n'écris pas toujours de code de bibliothèque, l'extensibilité n'est donc pas si importante, mais la testabilité est importante lorsque je refactore. Quoi qu'il en soit, le code testable produit 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 claire entre les singletons et les singletons (avec ou sans un 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 n'implémente pas 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 du modèle classique (anti-). Fondamentalement, 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 DB soit utilisée par toutes vos classes, il est préférable de le 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 nous avons également un bon moyen d'injecter un faux ou un stub 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 à réfléchir à 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) des membres statiques est lorsque je sais que le champ respectif aura la même valeur cross-instance. À 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 propriété statique 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 un moment que je ne les ai pas utilisés, donc 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 des propriétés statiques devraient être des fonctions. PHP a des fonctions et nous devrions les utiliser.

Ionuț G. Stan
la source
1
@Ionut, je n'en doute pas du tout. Je me rends compte que le poste / rôle a également de la valeur; cependant, comme tout ce qui concerne XP / Agile, il a un nom vraiment floozy.
jason
2
«Injection de dépendance», je crois, est le terme à consonance trop compliqué pour cela. BEAUCOUP et beaucoup de lecture à ce sujet partout dans SO et google-land. En ce qui concerne les "singletons", voici quelque chose qui m'a vraiment fait réfléchir: slideshare.net/go_oh/ ... Un exposé sur les raisons pour lesquelles les singletons PHP n'ont vraiment aucun sens. J'espère que cela contribue un peu à une vieille question :)
dudewad
22

Ainsi, en PHP, la statique peut être appliquée 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. Alors inventons une classe appelée BlogPost.

titleserait un membre non statique. Il contient le titre de cet article de blog. Nous pourrions également 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 ceci:

class blog_post {
    public $title;
    public $my_dao;

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

D'un autre côté, en utilisant des fonctions statiques, vous pouvez écrire une classe comme celle-ci:

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

Dans ce cas, puisque la fonction est statique et n'agit sur aucun article de blog en particulier, vous devez passer l'article de blog en tant qu'argument.

Fondamentalement, c'est 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 arguments.


Mise à jour: j'ajouterais également que la décision se situe rarement entre les méthodes d'instance et les méthodes statiques, et plus entre l'utilisation de classes et l'utilisation de tableaux associatifs. Par exemple, dans une application de blog, vous lisez des articles de blog à partir de la base de données et les convertissez en objets, ou vous les laissez dans le jeu de 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 BlogPostclasse qui agissent sur des publications individuelles, et vous écrivez des méthodes statiques qui agissent sur des collections de publications.

Rafe
la source
4
Cette réponse est assez bonne, surtout avec la mise à jour que vous avez faite, car c'est la raison pour laquelle je me suis retrouvé sur cette question. Je comprends la fonctionnalité de "::" vs "$ this", mais lorsque vous ajoutez en compte, l'exemple que vous avez donné des articles de blog extraits d'une base de données dans un tableau associatif. Cela ajoute une toute nouvelle dimension, c'est-à-dire également dans le ton sous-jacent de cette question.
Gerben Jacobs
C'est l'une des meilleures réponses pratiques (par rapport à la théorie) à cette question PHP très posée, l'addendum est très utile. Je pense que c'est la réponse que beaucoup de programmeurs PHP recherchent, votée!
ajmedway
14

Est-ce juste du style?

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

troelskn
la source
13

J'ai une approche différente de la plupart des réponses ici, en particulier lors de l'utilisation de 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

Permettez-moi de prendre un exemple. Puisque chaque script PHP produit du code HTML, mon framework a une classe d'écrivain html. Cela garantit qu'aucune autre classe ne tentera d'écrire du HTML car il s'agit d'une tâche spécialisée qui doit être concentrée dans une seule classe.

En règle générale, 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 pour 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 chaque classe qui a besoin d'écrire un tout petit peu de html doive gérer une instance de l'écrivain html.

Maintenant, je vais vous donner un exemple de ne pas utiliser de classes statiques. Ma classe de formulaire gère une liste d'éléments de formulaire tels que les entrées de texte, les 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 n'y a aucun moyen de faire cela avec des classes statiques, d'autant plus que certains des constructeurs de chaque classe ajoutée font beaucoup de travail. De plus, la chaîne d'héritage pour tous les éléments est assez complexe. C'est donc un exemple clair où les classes statiques ne doivent pas être utilisées.

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

JG Estiot
la source
pouvez-vous écrire un exemple du point ci-dessous "Certaines parties de votre code ne peuvent pas partager les variables de classe avec d'autres parties" #JG Estiot
khurshed alam
10

"Avoir un appel de méthode statique dans 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 dans le sujet, mais que les données d'instance sont totalement inappropriées, je préférerais de loin les avoir définies dans une classe et non chacune d'elles dans l'espace de noms global. J'utilise juste les mécanismes disponibles dans PHP5 pour

  • leur donner à tous un espace de noms - en évitant tout conflit de noms
  • gardez-les physiquement situés ensemble au lieu de se disperser dans un projet - les autres développeurs peuvent trouver plus facilement ce qui est déjà disponible et sont moins susceptibles de réinventer la roue
  • laissez-moi utiliser des consts de classe au lieu de définitions globales pour toutes les valeurs magiques

c'est tout simplement un moyen pratique d'imposer une cohésion plus élevée et un couplage plus faible.

Et FWIW - il n'y a pas une telle chose, au moins en PHP5, comme "classes statiques"; les méthodes et les propriétés peuvent être statiques. Pour éviter l'instanciation de la classe, on peut également la déclarer abstraite.

Grantwparks
la source
2
"Pour éviter l'instanciation de la classe, on peut aussi la déclarer abstraite" - j'aime beaucoup ça. J'ai déjà lu sur les gens qui font de __construct () une fonction privée, mais je pense que si j'en ai besoin, j'utiliserai probablement un résumé
Kavi Siegel
7

Demandez-vous d'abord ce que cet objet va représenter? Une instance d'objet est idéale pour fonctionner sur des ensembles séparés de données dynamiques.

Un bon exemple serait l'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);

Maintenant, dans ce package / 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 désormais 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 opérer sur chaque ligne indépendamment les unes des autres:

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 seul registre ou une session par utilisateur.

Dans une partie de votre candidature:

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

Et dans une autre partie:

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

Puisqu'il n'y aura qu'un seul utilisateur à la fois par session, il est inutile de créer une instance pour la session.

J'espère que cela aidera, ainsi que les autres réponses, à clarifier les choses. En remarque, consultez « cohésion » et « couplage ». Ils décrivent quelques très, très bonnes pratiques à utiliser lors de l'écriture de votre code qui s'appliquent à tous les langages de programmation.

Tres
la source
6

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

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

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

Muhammad Hasan Khan
la source
3

En général, vous devez utiliser des variables membres et des fonctions membres, sauf si elles doivent absolument être partagées entre toutes les instances ou à moins que vous ne créiez 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, alors que vous ne pouvez avoir qu'une seule copie des données sur lesquelles vous opérez si vous utilisez des données et des fonctions statiques. De plus, bien que cela ne soit pas aussi applicable à PHP, les fonctions statiques et les données font que le code n'est pas réentrant, alors que les données de classe facilitent la réentrance.

Michael Aaron Safyan
la source
3

Je voudrais dire qu'il y a certainement un cas où je voudrais des variables statiques - dans des applications multilingues. Vous pourriez avoir une classe à laquelle vous passez une langue (par exemple $ _SESSION ['language']) et elle accède à son tour à d'autres classes qui sont conçues comme ceci:

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

L'utilisation de Strings :: getString ("somestring") est une bonne manière d'abstraire votre utilisation du langage de 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 la classe Strings accède fonctionne plutôt bien.

dudewad
la source