Je comprends le concept d'un objet, et en tant que programmeur Java, je pense que le paradigme OO me vient assez naturellement dans la pratique.
Mais récemment, je me suis retrouvé à penser:
Attendez une seconde, quels sont réellement les avantages pratiques de l'utilisation d'un objet par rapport à l'utilisation d'une classe statique (avec des pratiques d'encapsulation et OO appropriées)?
Je pourrais penser à deux avantages de l'utilisation d'un objet (les deux sont significatifs et puissants):
Polymorphisme: permet d'échanger les fonctionnalités de manière dynamique et flexible pendant l'exécution. Permet également d'ajouter facilement de nouvelles «fonctionnalités» et alternatives au système. Par exemple, s'il existe une
Car
classe conçue pour fonctionner avec desEngine
objets et que vous souhaitez ajouter un nouveau moteur au système que la voiture peut utiliser, vous pouvez créer une nouvelleEngine
sous-classe et simplement passer un objet de cette classe dans l'Car
objet, sans avoir à changer quoi que ce soitCar
. Et vous pouvez décider de le faire pendant l'exécution.Pouvoir «transmettre des fonctionnalités»: vous pouvez passer un objet autour du système de manière dynamique.
Mais y a-t-il d'autres avantages aux objets par rapport aux classes statiques?
Souvent, lorsque j'ajoute de nouvelles «pièces» à un système, je le fais en créant une nouvelle classe et en instanciant des objets à partir de celui-ci.
Mais récemment, lorsque je me suis arrêté et y ai réfléchi, j'ai réalisé qu'une classe statique ferait exactement la même chose qu'un objet, dans de nombreux endroits où j'utilise normalement un objet.
Par exemple, je travaille sur l'ajout d'un mécanisme d'enregistrement / chargement de fichier à mon application.
Avec un objet, la ligne de code appelante ressemblera à ceci: Thing thing = fileLoader.load(file);
Avec une classe statique, cela ressemblerait à ceci: Thing thing = FileLoader.load(file);
Quelle est la différence?
Assez souvent, je n'arrive pas à penser à une raison pour instancier un objet alors qu'une classe statique ordinaire agirait de la même manière. Mais dans les systèmes OO, les classes statiques sont assez rares. Je dois donc manquer quelque chose.
Y a-t-il d'autres avantages pour les objets autres que les deux que j'ai énumérés? S'il vous plaît, expliquez.
EDIT: Pour clarifier. Je trouve les objets très utiles pour échanger des fonctionnalités ou pour transmettre des données. Par exemple, j'ai écrit une application qui compose des mélodies. MelodyGenerator
avait plusieurs sous-classes qui créent des mélodies différemment, et les objets de ces classes étaient interchangeables (modèle de stratégie).
Les mélodies étaient aussi des objets, car il est utile de les faire circuler. Les accords et les gammes aussi.
Mais qu'en est-il des parties «statiques» du système - qui ne seront pas transmises? Par exemple - un mécanisme de «sauvegarde de fichier». Pourquoi devrais-je l'implémenter dans un objet et non dans une classe statique?
la source
Thing
?FileLoader
contre celui qui lit à partir d'une prise? Ou une maquette pour les tests? Ou celui qui ouvre un fichier zip?System.Math
dans .NET est un exemple de quelque chose qui a beaucoup plus de sens en tant que classe statique: vous n'aurez jamais besoin de l'échanger ou de vous en moquer et aucune des opérations ne pourrait logiquement faire partie d'une instance. Je ne pense vraiment pas que votre exemple "d'économie" corresponde à ce projet de loi.Réponses:
lol tu parles comme une équipe sur laquelle je travaillais;)
Java (et probablement C #) supporte certainement ce style de programmation. Et je travaille avec des gens dont le premier instinct est: "Je peux en faire une méthode statique!" Mais il y a des coûts subtils qui vous rattrapent au fil du temps.
1) Java est un langage orienté objet. Et cela rend les gars fonctionnels dingues, mais vraiment ça tient assez bien. L'idée derrière OO est de regrouper les fonctionnalités avec l'état pour avoir de petites unités de données et des fonctionnalités qui préservent leur sémantique en masquant l'état et en exposant uniquement les fonctions qui ont du sens dans ce contexte.
En passant à une classe avec uniquement des méthodes statiques, vous cassez la partie "état" de l'équation. Mais l'État doit encore vivre quelque part. Donc, ce que j'ai vu au fil du temps, c'est qu'une classe avec toutes les méthodes statiques commence à avoir des listes de paramètres de plus en plus compliquées, car l'état se déplace de la classe vers les appels de fonction.
Après avoir créé une classe avec toutes les méthodes statiques, parcourez et examinez simplement combien de ces méthodes ont un seul paramètre commun. C'est un indice que ce paramètre doit être soit la classe conteneur pour ces fonctions, soit que ce paramètre doit être un attribut d'une instance.
2) Les règles d'OO sont assez bien comprises. Après un certain temps, vous pouvez regarder une conception de classe et voir si elle répond à des critères tels que SOLID. Et après de nombreux tests unitaires pratiques, vous développez une bonne idée de ce qui rend une classe «de bonne taille» et «cohérente». Mais il n'y a pas de bonnes règles pour une classe avec toutes les méthodes statiques, et il n'y a aucune vraie raison pour laquelle vous ne devriez pas tout regrouper. La classe est ouverte dans votre éditeur, alors que diable? Ajoutez-y simplement votre nouvelle méthode. Après un certain temps, votre application se transforme en un certain nombre "d'objets divins" concurrents, chacun essayant de dominer le monde. Encore une fois, les refactoriser en unités plus petites est très subjectif et difficile à dire si vous avez bien compris.
3) Les interfaces sont l'une des fonctionnalités les plus puissantes de Java. L'héritage de classe s'est avéré problématique, mais la programmation avec des interfaces reste l'une des astuces les plus puissantes du langage. (idem C #) Les classes entièrement statiques ne peuvent pas se placer dans ce modèle.
4) Il claque la porte sur des techniques OO importantes dont vous ne pouvez pas profiter. Vous pouvez donc travailler pendant des années avec seulement un marteau dans votre boîte à outils, sans réaliser à quel point les choses auraient été plus faciles si vous aviez eu un tournevis également.
4.5) Il crée les dépendances de compilation les plus dures et les plus incassables possibles. Ainsi, par exemple, si vous l'avez,
FileSystem.saveFile()
il n'y a aucun moyen de changer cela, à moins de simuler votre JVM au moment de l'exécution. Ce qui signifie que chaque classe qui fait référence à votre classe de fonction statique a une forte dépendance au moment de la compilation sur cette implémentation spécifique, ce qui rend l'extension presque impossible et complique énormément les tests. Vous pouvez tester la classe statique de manière isolée, mais il devient très difficile de tester les classes qui se réfèrent à cette classe de manière isolée.5) Vous rendrez fou vos collègues. La plupart des professionnels avec lesquels je travaille prennent leur code au sérieux et font attention à au moins un certain niveau de principes de conception. Mettre de côté l'intention de base d'un langage les amènera à se coiffer car ils vont constamment refactoriser le code.
Quand je suis dans une langue, j'essaie toujours de bien utiliser une langue. Ainsi, par exemple, lorsque je suis en Java, j'utilise une bonne conception OO, car je tire vraiment parti du langage pour ce qu'il fait. Quand je suis en Python, je mélange des fonctions de niveau module avec des classes occasionnelles - je ne pouvais écrire que des classes en Python, mais je pense que je n'utiliserais pas le langage pour ce qu'il est bon.
Une autre tactique consiste à mal utiliser une langue, et ils se plaignent de tous les problèmes qu'elle cause. Mais cela s'applique à peu près à toutes les technologies.
La principale caractéristique de Java est la gestion de la complexité dans de petites unités testables qui se tiennent ensemble afin qu'elles soient faciles à comprendre. Java met l'accent sur des définitions d'interface claires indépendamment de l'implémentation - ce qui est un énorme avantage. C'est pourquoi il (et d'autres langages OO similaires) restent si largement utilisés. Malgré toute la verbosité et le ritualisme, quand j'en ai fini avec une grosse application Java, j'ai toujours l'impression que les idées sont plus nettement séparées dans le code que mes projets dans un langage plus dynamique.
C'est difficile, cependant. J'ai vu des gens avoir le bug "tout statique", et c'est un peu difficile de les en dissuader. Mais je les ai vus ressentir un grand soulagement lorsqu'ils s'en remettent.
la source
Tu as demandé:
Avant cette question, vous avez répertorié le polymorphisme et le fait d' être transmis comme deux avantages de l'utilisation d'objets. Je tiens à dire que ce sont les caractéristiques du paradigme OO. L'encapsulation est une autre caractéristique du paradigme OO.
Cependant, ce ne sont pas les avantages. Les avantages sont les suivants:
Tu as dit:
Je pense que vous avez là un argument valable. À la base, la programmation n'est rien d'autre que la transformation des données et la création d'effets secondaires basés sur les données. Parfois, la transformation de données nécessite des données auxiliaires. D'autres fois, ce n'est pas le cas.
Lorsque vous traitez avec la première catégorie de transformations, les données auxiliaires doivent être transmises en entrée ou stockées quelque part. Un objet est la meilleure approche que les classes statiques pour de telles transformations. Un objet peut stocker les données auxiliaires et les utiliser au bon moment.
Pour la deuxième catégorie de transformations, une classe statique est aussi bonne qu'un objet, sinon mieux. Les fonctions mathématiques sont des exemples classiques de cette catégorie. La plupart des fonctions de bibliothèque C standard entrent également dans cette catégorie.
Tu as demandé:
Si
FileLoader
ne pas, jamais , besoin de stocker des données, j'irais avec la deuxième approche. S'il y a une petite chance qu'il aura besoin de données auxiliaires pour effectuer l'opération, la première approche est un pari plus sûr.Tu as demandé:
J'ai énuméré les avantages (avantages) de l'utilisation du paradigme OO. J'espère qu'ils s'expliquent d'eux-mêmes. Sinon, je serai heureux de développer.
Tu as demandé:
Ceci est un exemple où une classe statique ne fera tout simplement pas l'affaire. Il existe plusieurs façons de sauvegarder vos données d'application dans un fichier:
La seule façon de fournir de telles options est de créer une interface et de créer des objets qui implémentent l'interface.
En conclusion
Utilisez la bonne approche pour le problème à résoudre. Il vaut mieux ne pas être religieux au sujet d'une approche contre l'autre.
la source
Melody
,Chord
,Improvisations
,SoundSample
et d' autres), alors il sera économique de simplifier la mise en œuvre par des abstractions.Cela dépend parfois de la langue et du contexte. Par exemple, les
PHP
scripts utilisés pour traiter les demandes ont toujours une demande à traiter et une réponse à générer pendant toute la durée de vie du script, des méthodes statiques pour agir sur la demande et générer une réponse peuvent donc être appropriées. Mais dans un serveur écritNode.js
, il peut y avoir de nombreuses demandes et réponses différentes en même temps. La première question est donc - êtes-vous sûr que la classe statique correspond vraiment à un objet singleton?Deuxièmement, même si vous avez des singletons, l'utilisation d'objets vous permet de profiter du polymorphisme en utilisant des techniques telles que Dependency_injection et Factory_method_pattern . Ceci est souvent utilisé dans divers modèles Inversion_of_control et utile pour créer des objets fictifs pour les tests, la journalisation, etc.
Vous avez mentionné les avantages ci-dessus, alors voici celui que vous n'avez pas: l'héritage. De nombreux langages n'ont pas la possibilité de remplacer les méthodes statiques de la même manière que les méthodes d'instance peuvent être remplacées. En général, il est beaucoup plus difficile d'hériter et de remplacer les méthodes statiques.
la source
En plus du poste de Rob Y
Tant que la fonctionnalité de votre
load(File file)
peut être clairement séparée de toutes les autres fonctions que vous utilisez, il est correct d'utiliser une méthode / classe statique. Vous extériorisez l'état (ce qui n'est pas une mauvaise chose) et vous n'obtenez pas non plus de redondance car vous pouvez par exemple appliquer partiellement ou curry votre fonction afin de ne pas avoir à vous répéter. (qui est en fait identique ou similaire à l'utilisation d'unfactory
modèle)Cependant, dès que deux de ces fonctions commencent à avoir une utilisation commune, vous voulez pouvoir les regrouper d'une manière ou d'une autre. Imaginez que vous ayez non seulement une
load
fonction mais aussi unehasValidSyntax
fonction. Que vas-tu faire?Voir les deux références
myfile
ici? Vous commencez à vous répéter car votre état externalisé doit être transmis pour chaque appel. Rob Y a décrit comment vous intériorisez l'état (ici lefile
) afin que vous puissiez le faire comme ceci:la source
hasValidSyntax()
etload()
ne sont pas autorisés à l' état de réutilisation (le résultat d'un fichier partiellement analysé).Un problème majeur lors de l'utilisation de classes statiques est qu'elles vous obligent à masquer vos dépendances et vous obligent à dépendre des implémentations. Dans la signature de constructeur suivante, quelles sont vos dépendances:
Eh bien, à en juger par la signature, une personne a juste besoin d'un nom, non? Eh bien, pas si la mise en œuvre est:
Donc, une personne n'est pas seulement instanciée. Nous tirons en fait des données d'une base de données, ce qui nous donne un chemin vers un fichier, qui doit ensuite être analysé, puis les vraies données que nous voulons doivent être révélées. Cet exemple est clairement au-dessus, mais il prouve le point. Mais attendez, il peut y en avoir bien plus! J'écris le test suivant:
Cela devrait cependant passer, non? Je veux dire, nous avons encapsulé toutes ces autres choses, non? Eh bien, en fait, il explose avec une exception. Pourquoi? Oh, oui, les dépendances cachées que nous ne connaissions pas ont un état global. Les
DBConnection
besoins initialisés et connectés. LesFileLoader
besoins initialisés avec unFileFormat
objet (commeXMLFileFormat
ouCSVFileFormat
). Vous n'en avez jamais entendu parler? Eh bien, c'est le point. Votre code (et votre compilateur) ne peut pas vous dire que vous avez besoin de ces choses car les appels statiques masquent ces dépendances. Ai-je dit test? Je voulais dire que le nouveau développeur junior vient de livrer quelque chose comme ça dans votre dernière version. Après tout, compilé = fonctionne, non?En outre, supposons que vous utilisez un système sur lequel aucune instance MySQL n'est en cours d'exécution. Ou, disons que vous êtes sur un système Windows mais que votre
DBConnection
classe ne sort sur votre serveur MySQL que sur une box Linux (avec des chemins Linux). Ou, disons que vous êtes sur un système où le chemin renvoyé par leDBConnection
n'est pas en lecture / écriture pour vous. Cela signifie qu'essayer d'exécuter ou de tester ce système dans l'une de ces conditions échouera, non pas à cause d'une erreur de code, mais à cause d'une erreur de conception qui limite la flexibilité du code et vous lie à une implémentation.Maintenant, disons, nous voulons enregistrer chaque appel à la base de données pour une
Person
instance spécifique , une qui passe par un certain chemin problématique. Nous pourrions mettre la connexionDBConnection
, mais cela enregistrera tout , mettant beaucoup d'encombrement et rendant difficile de distinguer le chemin de code particulier que nous voulons tracer. Si, cependant, nous utilisons l'injection de dépendances avec uneDBConnection
instance, nous pourrions simplement implémenter l'interface dans un décorateur (ou étendre la classe, car nous avons les deux options disponibles avec un objet). Avec une classe statique, nous ne pouvons pas injecter la dépendance, nous ne pouvons pas implémenter une interface, nous ne pouvons pas l'envelopper dans un décorateur et nous ne pouvons pas étendre la classe. Nous ne pouvons l'appeler directement, quelque part caché au fond de notre code. Ainsi nous sommes forcés d'avoir une dépendance cachée sur une implémentation.Est-ce toujours mauvais? Pas nécessairement, mais il serait peut-être préférable de renverser votre point de vue et de dire "Y a-t-il une bonne raison pour laquelle cela ne devrait pas être un cas?" plutôt que « Y at - il une bonne raison pour laquelle ce devrait être une instance? » Si vous pouvez vraiment dire que votre code sera aussi inébranlable (et sans état) que
Math.abs()
dans son implémentation et la manière dont il sera utilisé, alors vous pouvez envisager de le rendre statique. Cependant, avoir des instances sur des classes statiques vous donne un monde de flexibilité qui n'est pas toujours facile à récupérer après coup. Il peut également vous donner plus de clarté sur la véritable nature des dépendances de votre code.la source
new
monter un tas d'objets dans le constructeur (ce qui n'est généralement pas conseillé), mais je peux aussi les injecter. Avec les classes statiques, il n'y a aucun moyen de les injecter (pas facilement en Java de toute façon, et ce serait une toute autre discussion pour les langages qui le permettent), donc il n'y a pas de choix. C'est pourquoi je souligne l'idée d'être obligé de cacher des dépendances si lourdement dans ma réponse.Class
, alors nous nous assurons que c'est la bonne classe) ou vous écrivez du canard (nous nous assurons qu'il répond à la bonne méthode). Si vous voulez faire ce dernier, alors vous devriez réévaluer votre choix de langue. Si vous voulez faire le premier, les instances fonctionnent très bien et sont intégrées.Juste mes deux cents.
Pour votre question:
Vous pouvez vous demander si le mécanisme de la partie du système qui lit les sons, ou la partie du système qui enregistre les données dans un fichier CHANGERA . Si votre réponse est oui, cela indique que vous devez les résumer en utilisant
abstract class
/interface
. Vous pouvez demander, comment puis-je savoir les choses futures? Certainement, nous ne pouvons pas. Donc, si la chose est sans état, vous pouvez utiliser une «classe statique» telle quejava.lang.Math
, sinon, utiliser une approche orientée objet.la source