Est-ce que je ne peux pas utiliser toutes les méthodes statiques?

64

Quelle est la différence entre les deux méthodes UpdateSubject ci-dessous? Je me suis senti mieux d'utiliser des méthodes statiques si vous voulez simplement opérer sur les entités. Dans quelles situations dois-je aller avec des méthodes non statiques?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

Je sais que la communauté suscitera de nombreux éloges pour cette question vraiment agaçante, mais je ne pouvais pas m'empêcher de la poser.

Est-ce que cela devient impraticable en matière d'héritage?

Mise à jour:
Cela se passe sur notre lieu de travail maintenant. Nous travaillons sur une application Web asp.net de 6 mois avec 5 développeurs. Notre architecte a décidé d'utiliser toutes les méthodes statiques pour toutes les API. Son raisonnement étant que les méthodes statiques sont légères et qu'il profite aux applications Web en réduisant la charge du serveur.

Alexandre
la source
9
Rich Hickey encouragerait quelque chose de non idiomatique comme ça (toutes les méthodes statiques). Si vous aimez un style fonctionnel, vous pouvez également utiliser un langage fonctionnel;) Dans le logiciel, tout n'est pas mieux modélisé sous forme d'objet.
Job
13
@Job: Je ne vois pas vraiment comment cela fonctionne, c'est procédural.
Aaronaught
2
@Job: Cette classe n'est pas immuable.
Aaronaught
3
@DonalFellows: Bien sûr que vous pouvez, C # et F # sont tous deux un paradigme mixte. Mais le fait demeure, méthodes statiques! = Programmation fonctionnelle. La PF désigne les fonctions de transmission / chaînage, les types de données immuables, la prévention des effets secondaires, etc. C'est tout le contraire de ce que l'extrait de code fait dans l'OP.
Aaronaught
4
"Son raisonnement étant que les méthodes statiques sont légers et qu'il profite aux applications Web en réduisant la charge du serveur". C'est faux. Garder le serveur chargé?
Mamcx

Réponses:

87

Je vais aller avec les problèmes les plus évidents de méthodes statiques avec des paramètres explicites "this":

  1. Vous perdez la répartition virtuelle et par la suite le polymorphisme. Vous ne pouvez jamais remplacer cette méthode dans une classe dérivée. Bien sûr, vous pouvez déclarer une méthode new( static) dans une classe dérivée, mais tout code y accédant doit connaître la hiérarchie entière de la classe et procéder à une vérification et à une conversion explicites, ce qui est précisément ce que OO est censé éviter .

  2. Sorte d'une extension de # 1, vous ne pouvez pas remplacer les instances de la classe par une interface , car les interfaces (dans la plupart des langues) ne peuvent pas déclarer de staticméthodes.

  3. La verbosité inutile. Lequel est le plus lisible: Subject.Update(subject)ou juste subject.Update()?

  4. Vérification d'argument. Là encore, cela dépend de la langue, mais beaucoup compileront une vérification implicite pour s’assurer que l’ thisargument n’est pas nullutilisé pour empêcher un bogue de référence null de créer des conditions d’exécution non sûres (sorte de dépassement de mémoire tampon). N'utilisant pas de méthodes d'instance, vous devez ajouter cette vérification explicitement au début de chaque méthode.

  5. C'est confu. Quand un programmeur normal et raisonnable voit une staticméthode, il va naturellement supposer qu’elle n’exige pas d’instance valide (à moins qu’elle prenne plusieurs instances, comme une méthode de comparaison ou d’égalité, ou puisse fonctionner sur des nullréférences). . Voir les méthodes statiques utilisées de cette façon va nous faire faire une double ou peut-être une triple prise, et après la 4ème ou 5ème fois, nous allons être stressés et en colère et dieu vous aidera si nous connaissons votre adresse personnelle.

  6. C'est une forme de duplication. En réalité, lorsque vous appelez une méthode d'instance, le compilateur ou le moteur d'exécution la recherche dans la table de méthodes du type et l'invoque thissous forme d'argument. Vous ré-implémentez ce que le compilateur fait déjà. Vous violez DRY , répétez le même paramètre encore et encore selon différentes méthodes lorsque vous n'en avez pas besoin.

Il est difficile de concevoir une bonne raison de remplacer les méthodes d'instance par des méthodes statiques. S'il vous plaît ne pas.

Aaronaught
la source
5
+1 pour votre dernière ligne seule. Je souhaite que beaucoup plus de gens pensent "méthode d'instance" avant de penser "méthode statique".
Stuart Leyland-Cole
4
+1 J'ajouterais que les méthodes statiques rendent la rédaction des tests difficile, voire impossible. Dans la plupart des cas, vous ne pouvez pas vous moquer de vous: une fois qu'une partie de votre code dépend d'une méthode statique, vous ne pouvez pas la remplacer par un non. op dans vos tests. Un premier exemple dans le monde .NET serait classes comme File, FileInfoet DirectoryInfo(et en fait, il y a des bibliothèques qui les cachent derrière des interfaces qui peuvent être injectés au besoin et raillé dans les tests).
sm
Je pense que vos arguments sur le polymorphisme / héritage sont sans objet. il ne faut pas utiliser l'héritage pour la réutilisation du code, ce n'est donc pas pertinent. la partie héritage est également douteuse. dans la plupart des langages modernes, les signatures de méthode sont polymorphes en elles-mêmes. Si vous adoptez une approche fonctionnelle, vous commencez à transmettre des méthodes et vous composez des méthodes complexes à partir de méthodes simples et interchangeables en fonction de leur interface. Il n’ya fondamentalement aucun inconvénient aux méthodes statiques qui ne sont pas compensées par une simple conception en pensant à eux, comme avec OOP.
Sara
@ ms ce n'est tout simplement pas vrai. si vous avez une dépendance forte à n'importe quoi, statique ou non, cela ne peut pas être dans un test unitaire, alors c'est indestimable, un point c'est tout. statique ne cause pas cela. une méthode statique peut être injectée de la même manière qu'une classe représentant une connexion à une base de données. vous supposez que "statique" signifie "statique, plus des dépendances cachées qui touchent l'état global et les ressources externes". ce n'est pas juste.
Sara
@kai essayez d'injecter une méthode statique qui fait Y à la place de X, quels que soient X et Y. Vous n'avez pas besoin de dépendances sur quoi que ce soit d'extérieur pour être vissé. Peut-être êtes-vous simplement intéressé à tester un aspect ne nécessitant pas de longs calculs X à effectuer. Comment injecter votre méthode statique fantaisie sans créer de wrapper? Le transfert de fonctions n'est pas pris en charge par toutes les langues. Les méthodes statiques ont leurs cas d'utilisation et je les utilise lorsqu'il est logique de le faire . C’est, comme il arrive toujours, un cas de "juste parce que vous le pouvez ne signifie pas nécessairement que vous devriez".
sm
13

Vous avez à peu près décrit exactement comment vous faites la POO en C, dans lequel seules les méthodes statiques sont disponibles, et "this" est un pointeur struct. Et oui, vous pouvez effectuer un polymorphisme d’exécution en C en spécifiant qu’un pointeur de fonction pendant la construction doit être stocké en tant qu’un des éléments de la structure. Les méthodes d'instance disponibles dans d'autres langues ne sont que des sucres syntaxiques qui font essentiellement la même chose sous le capot. Certaines langues, comme python, sont à mi-chemin, le paramètre "self" est explicitement répertorié, mais une syntaxe spéciale pour l'appeler gère l'héritage et le polymorphisme pour vous.

Karl Bielefeldt
la source
FWIW, avec C, vous pouvez effectuer un envoi virtuel comme ceci: obj->type->mthd(obj, ...);et cela est sensiblement similaire à ce qui se produit avec l’envoi virtuel dans d’autres langues (mais en coulisse).
Donal Fellows
@ Karl Pouvez-vous s'il vous plaît fournir une sorte de référence écrite pour commencer à creuser plus profondément?
Jorge Lavín
Vous voudrez peut-être considérer GObject comme une implémentation de référence efficace.
Karl Bielefeldt
10

Le problème avec la méthode statique vient au moment où vous avez besoin d'une sous-classe.

Les méthodes statiques ne peuvent pas être remplacées dans les sous-classes. Par conséquent, vos nouvelles classes ne peuvent pas fournir de nouvelles implémentations des méthodes, ce qui les rend moins utiles.


la source
2
Ce n'est pas nécessairement un problème. Certains langages fournissent d'autres mécanismes (méthodes non virtuelles en C ++ et C #) pour accomplir la même chose intentionnellement - C # fournit même des méthodes "scellées" pour étendre encore cette idée! Évidemment, vous ne le feriez pas pour le diable, mais c’est une bonne technique à prendre en compte ...
Shog9
@Steve, cela ne remplace pas, car vous pouvez toujours appeler les deux méthodes directement.
@Steve, en Java, les méthodes statiques ne peuvent pas être remplacées.
@ Thorbjørn - premièrement, la question n'est pas balisée avec Java, ni avec aucun autre langage POO spécifique. Plus important encore, je n'ai pas dit que vous pouviez remplacer une fonction de membre statique. J'essayais d'utiliser une analogie pour faire valoir un point. Comme j'ai clairement échoué, les commentaires ont été supprimés.
Steve314
2
En un sens, il s'agit toujours de "nouvelles implémentations de méthodes". Mais pas au sens de la POO. C'est un peu comme si je disais "ton libellé n'est pas parfait non plus, nur-nur-na-nur-nur" ;-)
Steve314 Le
7

Si vous déclarez une méthode static, vous n'avez pas besoin d'instancier un objet de la classe (à l'aide du newmot clé) pour exécuter la méthode. Toutefois, vous ne pouvez faire référence à aucune variable membre, sauf si elles sont également statiques. Dans ce cas, ces variables membres appartiennent à la classe et non à un objet instancié spécifique de la classe.

En d'autres termes, le staticmot - clé fait en sorte qu'une déclaration fasse partie de la définition de la classe. Ainsi, elle devient un point de référence unique pour toutes les occurrences de la classe (objets instanciés à partir de la classe).

Il s’ensuit que si vous souhaitez que plusieurs copies en cours d’exécution de votre classe ne soient pas partagées entre toutes vos copies en cours d’exécution (c’est-à-dire que chaque objet possède son propre état unique), ne peut pas déclarer ces variables membres statiques.

En général, vous ne devez utiliser des staticclasses et des méthodes que lorsque vous créez des méthodes utilitaires acceptant un ou plusieurs paramètres et renvoyant un objet de quelque sorte que ce soit, sans aucun effet secondaire (par exemple, modification des variables d'état dans la classe).

Robert Harvey
la source
1
Je pense que le PO comprend ce que staticsignifie, il demande pourquoi ne pas tout déclarer comme statique.
Ed S.
1
Il est passe dans une instance de Subject- mais comme un paramètre explicite, au lieu de l'implicite thisparamètre.
Steve314
Eh bien, oui ... mais je ne vois pas ce que tu veux dire, je suppose.
Ed S.
@Ed - seulement que vous pouvez faire à peu près n'importe quoi avec un paramètre explicite que vous pourriez utiliser avec un thisparamètre implicite . Il existe des différences dans les attentes, mais en dehors de l'héritage, il n'y a pas de réelle différence dans ce que vous pouvez faire. Par exemple, vous pouvez accéder à ces membres non statiques via le paramètre explicit.
Steve314
Je fonctionnais en partant du principe que vous ne pouviez pas obtenir l'argument en C # des membres privés, même par le biais d'une méthode statique déclarée dans la même classe (comme vous pouvez le faire en C ++). Je ne sais pas pourquoi je pensais cela, mais je me suis trompé.
Ed S.
3

La raison pour laquelle les gens soutiennent que "les méthodes statiques montrent une mauvaise conception" n'est pas nécessairement due aux méthodes, mais bien à des données statiques, implicites ou explicites. Par implicite, je veux dire TOUT état dans le programme qui n'est pas contenu dans les valeurs de retour ou les paramètres. La modification d'état dans une fonction statique est un retour à la programmation procédurale. Toutefois, si la fonction ne modifie pas l'état ou ne délègue pas la modification d'état aux fonctions d'objet composant, il s'agit en réalité d'un paradigme fonctionnel plutôt que procédural.

Si vous ne changez pas d'état, les méthodes et les classes statiques peuvent présenter de nombreux avantages. Il est beaucoup plus facile de s’assurer que la méthode est pure, et donc exempte d’effets secondaires et que les erreurs éventuelles sont entièrement reproductibles. Cela garantit également que différentes méthodes utilisant plusieurs bibliothèques utilisant une bibliothèque partagée peuvent être assurées qu'elles obtiendront les mêmes résultats avec la même entrée, ce qui facilite beaucoup le suivi des erreurs et les tests unitaires.

En fin de compte, il n'y a pas de différence de pratique entre les objets surdimensionnés couramment vus tels que 'StateManager' et les données statiques ou globales. L'avantage des méthodes statiques utilisées correctement est qu'elles peuvent indiquer l'intention de l'auteur de ne pas modifier l'état à des réviseurs ultérieurs.

Mathieson
la source
1
Votre déclaration "... est un retour à la programmation procédurale ..." me fait penser que vous le considérez si mal, je suppose que cela fait partie de OO dans un sens.
NoChance
making ... unit testing much easier.Non ça ne va pas. Il fait fait tout code qui appelle des méthodes statiques impossible à l' unité test, puisque vous ne pouvez pas l' isoler de la méthode statique. Un appel à une méthode statique devient une dépendance codée en dur à cette classe.
StuperUser
2

Selon la langue et ce que vous essayez de faire, la réponse peut être qu'il n'y a pas beaucoup de différence, mais la staticversion est un peu plus encombrée.

Comme votre exemple de syntaxe le suggère en Java ou en C #, je pense que Thorbjørn Ravn Andersen a raison de signaler le problème fondamental (avec la liaison tardive). Avec une méthode statique, il n'y a pas de paramètre "spécial" sur lequel une liaison tardive doit être basée, vous ne pouvez donc pas avoir de liaison tardive.

En effet, une méthode statique est simplement une fonction dans un module, ce module ayant le même nom que la classe - il ne s'agit pas vraiment d'une méthode de POO.

Steve314
la source
2

Vous êtes en train de vaincre la totalité des objets lorsque vous faites cela. Il y a une bonne raison pour laquelle nous avons quasiment changé de code de procédure au code orienté objet.

Je ne déclarerais JAMAIS une méthode statique prenant le type de classe en tant que paramètre. Cela rend le code plus complexe sans aucun gain.

Loren Pechtel
la source
3
Vous pourriez le faire si vous passiez une méthode objectà une staticméthode et retourniez une nouvelle object. Les programmeurs fonctionnels le font tout le temps.
Robert Harvey
question: considérez-vous la Mathclasse statique "complexe sans gain" ou préférez-vous que tous les types de nombres aient des méthodes virtuelles définissant la valeur absolue, la négation, l'ajout, la quadrature, les racines arbitraires, etc.? Je ne pense pas. les objets sont propres lorsque vous devez stocker un état mutable masqué où vous devez appliquer des invariants. en mélangeant chaque méthode concievable qui opère sur un type dans le type lui-même, c’est quelque chose qui conduit à un code complexe et non maintenable. Je suis d'accord avec Robert, vous voudrez peut-être regarder comment fonctionnent les programmeurs, cela peut être une véritable révélation!
Sara
@ Kai Comme je l'ai dit, à peu près. La classe Math est une exception raisonnable, bien que l’attacher aux types numériques ne serait pas une mauvaise idée.
Loren Pechtel
2

Il y a beaucoup de bonnes réponses ici, et elles sont beaucoup plus perspicaces et mieux informées que tout ce que je pourrais jamais trouver comme réponse, mais je sens qu'il y a un petit quelque chose qui n'est pas abordé:

"Notre architecte a décidé que nous utilisions toutes les méthodes statiques pour toutes les API. Son raisonnement étant léger, il favorise les applications Web en réduisant la charge du serveur ."

(l'accent gras est le mien)

Mon 2c sur cette partie de la question est la suivante:

Théoriquement, ce qui est dit là est vrai: l'appel d'une méthode statique n'engendre que la surcharge de cette méthode. L'appel d'une méthode non statique (instance) entraîne la surcharge supplémentaire liée à l'instanciation initiale de l'objet et, à un moment donné, à la destruction de l'instance (manuellement ou via un certain type de garbage collection automatique, en fonction de la plate-forme utilisée).

La pièce un peu de l'avocat du diable à ce sujet: nous pouvons aller encore plus loin et dire des choses telles que:

  • cela peut devenir très grave si une instance est créée pour chaque invocation de la méthode (instance) (au lieu de simplement la laisser statique et de l'appeler ainsi)

  • En fonction de la complexité du constructeur, du type hyerarchy, des autres membres de l'instance et d'autres facteurs inconnus, la surcharge supplémentaire liée à l'appel d'une méthode non statique peut varier et devenir très importante.

En vérité, à mon humble avis, les points ci-dessus (et l'approche générale consistant à "utilisons la statique parce qu'ils sont plus rapides") sont des arguments / évaluations de l'homme de paille:

  • si le code est bon et, plus spécifique à cet argument, si les instances ne sont créées que lorsque cela est nécessaire (et détruites de manière optimale), vous n’obtenez pas de surcharge supplémentaire lorsque vous utilisez des méthodes d’insance lorsque cela est approprié (parce que si ne crée que les objets nécessaires, ceux-ci auraient été créés même si cette méthode avait été déclarée statique quelque part où = surcharge du même instanciation)

  • en abusant de cette façon de la déclaration statique des méthodes, il est possible de masquer certains problèmes liés à la manière dont les instances sont créées (étant donné que la méthode est statique, un code d’instanciation incorrect risque de passer inaperçu plus tard, et ce n’est jamais bon).

Shivan Dragon
la source
2

C'est une question très intéressante qui a tendance à avoir des réponses très différentes selon la communauté à laquelle vous le posez. D'autres réponses semblent être le biais C # ou Java: un langage de programmation (principalement) orienté objet pur et ayant une sorte de vue idiomatique de ce qu'est l'orientation objet.

Dans d'autres communautés, telles que C ++, l'orientation des objets est interprétée avec plus de libéralité. Certains experts bien connus ont étudié le sujet et, en fait, concluent que les fonctions libres améliorent l’encapsulation (c’est le mien).

Nous avons maintenant vu qu'un moyen raisonnable d'évaluer la quantité d'encapsulation dans une classe consiste à compter le nombre de fonctions pouvant être rompues si la mise en œuvre de la classe change. Cela étant, il devient clair qu'une classe avec n fonctions membres est plus encapsulée qu'une classe avec n + 1 fonctions membres. Et cette observation est ce qui justifie mon argument pour préférer les fonctions non-membres non-amis aux fonctions membres

Scott Meyers

Pour ceux qui ne connaissent pas la terminologie C ++:

  • fonction membre = méthode
  • fonction non membre = méthode statique
  • non-ami = utiliser uniquement l'API publique
  • fonction non membre non membre = méthode statique utilisant uniquement l'API publique

Maintenant, pour répondre à votre question:

Est-ce que je ne peux pas utiliser toutes les méthodes statiques?

Non, vous ne devriez pas toujours utiliser des méthodes statiques.

Cependant, vous devez les utiliser chaque fois que vous devez uniquement utiliser l' API publique d'une classe.

authchir
la source
-3

en Java ...

Les méthodes statiques ne sont pas écrasées, mais cachées. Ce serait une grosse différence (problème?) Dans le cas de l'extension de la classe.

D'autre part, j'ai remarqué que les programmeurs Java ont tendance à penser que tout DOIT être objet, sans vraiment comprendre pourquoi, et je ne suis pas d'accord.

Static doit être utilisé pour le code spécifique à la classe. La statique ne fonctionne pas bien avec l'héritage.

Quelques bons exemples d'utilisation de méthodes statiques sont des fonctions indépendantes sans effets secondaires.

voir aussi le mot clé synchronisé ...

Nicolas
la source
2
Comment cette méthode statique se cache-t-elle et où cette différence et ce problème peuvent-ils émerger lors de l'extension de la classe? Comment synchronisé s'intègre-t-il dans cela? Où se posent les problèmes de statique et d'héritage? Pouvez-vous fournir quelques bonnes et mauvaises méthodes statiques dans les principales bibliothèques Java et expliquer pourquoi chacune d’elles est la bonne?