Principes de la POO et noms de méthode

22
class Boxer:

    def punch(self, punching_bag, strength):
        punching_bag.punch(strength)


class PunchingBag:

    def punch(self, strength):
        print "Punching bag punched with strength", strength

boxer = Boxer()
punching_bag = PunchingBag()

boxer.punch(punching_bag, 2)

Pas de doute, c'est punchun bon nom de méthode dans le cas d'un boxeur. Mais le nom est-il punchégalement bon pour la méthode du sac de boxe? Dans les deux cas, je veux dire le punch comme une commande (c'est-à-dire faire du punch).

climat
la source

Réponses:

23

Une bonne règle de base est que les noms de méthode doivent être des verbes ou des prédicats tels que l'objet sur lequel vous les appelez ( selfdans la convention Python standard, thisdans la plupart des autres langues) devient le sujet.

Par cette règle, file.closec'est un peu faux, sauf si vous choisissez le modèle mental selon lequel le fichier se ferme lui-même ou que l' fileobjet ne représente pas le fichier lui-même, mais plutôt un descripteur de fichier ou une sorte d'objet proxy.

Un sac de boxe ne se frappe jamais, donc punchingBag.punch()c'est faux de toute façon. be_punched()est techniquement correct, mais moche. receive_punch()pourrait fonctionner, ou handle_punch(). Une autre approche, assez populaire en JavaScript, consiste à traiter ces appels de méthode comme des événements, et la convention qui y est associée est le nom de l'événement, préfixé par «on», ce qui serait on_punched()ou on_hit(). Alternativement, vous pouvez adopter la convention selon laquelle les participes passés indiquent une voix passive, et selon cette convention, le nom de la méthode serait juste punched().

Un autre aspect à considérer est de savoir si le sac de boxe sait réellement ce qui l'a frappé: est-ce que cela fait une différence si vous le frappez, le battez avec un bâton ou le heurtez avec un camion? Si c'est ainsi, quelle est la différence? Pouvez-vous résumer la différence en un argument, ou avez-vous besoin de différentes méthodes pour différents types de punitions reçues? Une méthode unique avec un paramètre générique est probablement la solution la plus élégante, car elle maintient le couplage de degré bas, et une telle méthode ne devrait pas être appelée punched()ou handle_punch(), mais plutôt quelque chose de plus générique receive_hit(). Avec une telle méthode en place, vous pouvez implémenter toutes sortes d'acteurs qui peuvent frapper les sacs de boxe, sans changer le sac de boxe lui-même.

tdammers
la source
4
@Artur: oui et non. Les fichiers peuvent (conceptuellement parlant) se fermer lorsqu'ils sont demandés; les tableaux peuvent se trier; mais les sacs de boxe ne se perforent pas.
tdammers
2
D'accord, si notre sac de boxe frappe le mur à une vitesse folle, est-ce le mur qui l'a frappé, ou est-ce que le sac de boxe a été touché par lui-même et par lui-même?
1
@tdammers: Votre suggestion de généraliser pourrait également conduire à une interface appelée Hitable.
Jens Piegsa
2
@Artur: Je pense que c'est là que l'hypothèse OOP que chaque phrase a un sujet naturel, et que cette idée est applicable à la programmation, se décompose.
tdammers
1
La principale question est donc. Si les fichiers peuvent se fermer, les tableaux peuvent se trier, etc., pourquoi les sacs de boxe ne peuvent-ils pas se perforer eux-mêmes? Y a-t-il une réelle différence ou est-ce juste que dans le premier cas, nous y sommes habitués et dans le second, nous ne le sommes pas?
Clime
6

Je pense que c'est une question conceptuelle (comment nous pensons au monde). C'est bien de dire:

  • Regardez, la porte se ferme. door.close()
  • Wow, le papier se plie tout seul. paper.fold()
  • Que se passe-t-il?! Ce dossier sur le bureau vient de se fermer, personne n'est là. file.close()

C'est bizarre de dire:

  • Ce sac de boxe dans la salle de gym vient de se frapper. bag.punch()

Il aurait besoin d'avoir quelque chose pour se frapper en premier lieu (par exemple des bras). Vous diriez probablement:

  • Le sac de boxe a commencé à bouger tout seul comme quelqu'un l'aurait frappé. punching_bag.move()

Il est normal que les objets programmatiques fassent des choses que les autres font normalement avec / avec eux (dans le "monde réel"). Mais je suppose que cela devrait toujours avoir au moins un certain sens que la chose le fasse à / avec lui-même . Vous devriez pouvoir l'imaginer facilement sans devenir obscur (comme dans le cas du punching_bag).

climat
la source
2

C'est une question de goût, je pense. Punching bagLa punch()méthode de 's est au moins cohérente avec file.close()ou frame.move()dans le sens d'expérimenter une action sur elle-même. Une plus grande question serait, pourquoi a- Boxert-il une punch(something)méthode?


la source
J'aime votre point sur file.close (). C'était quelque chose que je voulais dire. Peut-être que le boxeur a une méthode de punch parce qu'il y a aussi un entraîneur qui forme le boxeur. Eh bien, en fait, j'essayais juste de donner un exemple d'une action (message) passée à travers plusieurs objets, le dernier étant "objet d'une action". J'ai un léger problème avec list.append (4), account.deposit (50), file.close (), paper.fold () vs boxer.punch (), dog.bark (), logger.log () etc. .
clime
Lors du passage à travers plusieurs objets, il y a 2 cas: vous utilisez un contexte lié (self), et vous ne l'utilisez pas. Si vous le faites, vos méthodes devraient être Coach.sayPunchToBoxer(), Boxer.punchNearestBag()et Bag.punch(). Sinon, vous devez deviner ce qui se passera à chaque appel Coach.punch(). La règle générale est la suivante: si l'objet qui subit une action n'est pas spécifié dans le nom de la méthode, le récepteur est cet objet.
Eh bien, je pense que c'est aussi bien: coach.say_punch (boxeur, punching_bag), boxer.punch (punching_bag). ie récepteur n'est pas dans le nom de la méthode mais dans les paramètres.
Clime
1
Bien sûr, je voulais dire que le récepteur d'action devrait être devinable à partir de la déclaration d'appel.
2

Vous avez deux messages différents: un pour commander un objet à poinçonner et un pour informer un objet qu'il a été poinçonné. Considérez qu'un objet Boxer va probablement avoir besoin de répondre aux deux . Différemment . C'est une très bonne raison de leur donner des noms différents.

Ma tendance serait de conserver punch(boxer, object, strength)et de renommer la méthode opposée à punched. Vous pouvez l'appeler handle_punchou quelque chose comme ça, mais il reste ambigu que ce soit pour gérer une commande de punch ou la notification de son punch.

user2313838
la source
Un bon point sur Boxer ayant besoin à la fois de punch et de quelque chose comme handle_punch (ce serait defenddans ce cas particulier). Mais le sac de boxe ne sera jamais bidirectionnel comme ça. Et il y a déjà ce fichier.close () ...
Clime
defendest une commande. C'est une action possible qu'un objet pourrait entreprendre en réponse punched, mais vous ne voudriez pas que d'autres objets invoquent defenddirectement.
user2313838
2

Votre approche conduira éventuellement à un code très couplé.

Pour résumer idéalement Eric Lippert ici, vous voudriez que le boxeur puisse frapper beaucoup de choses. Avoir le sac de boxe comme signature de la fonction boxeur implique que le boxeur soit créé avec une connaissance immédiate de Tout (c'est-à-dire perforable). De plus, donner un coup de poing et recevoir un coup de poing sont deux choses TRÈS différentes, donc ils ne devraient pas partager le même nom.

Je préfère modéliser cela comme un boxeur qui crée un coup de poing (un autre objet qui contient la force, la portée, la direction, etc.) du poinçon.

Ensuite, ayez le sac de boxe avec une méthode telle que onPunch qui reçoit cet objet de punch peut calculer l'effet du punch sur lui-même.

Gardant cela à l'esprit, le nom des choses compte beaucoup. Elle doit correspondre au modèle mental que vous avez de la situation. Si vous essayez d'expliquer comment quelque chose peut se produire qui n'a aucun sens à première vue, ou si vous avez du mal à nommer quelque chose, alors votre modèle est peut-être faux et doit changer.

Il est difficile de changer de modèle après avoir commencé, les gens ont généralement tendance à plier la réalité pour l'adapter au modèle. Le problème avec cela est que lorsque vous pliez des choses pour les ajuster (comme un sac de boxe qui peut perforer des choses), le monde que vous créez devient de plus en plus complexe et les interactions deviennent de plus en plus difficiles à mettre en œuvre. Vous finirez par arriver à un point où l'ajout, même le plus trivial, devient un cauchemar de changements et de bugs. Cette dette technique conceptuelle peut avoir un prix très élevé même si le coût initial était perçu à l'époque comme la chose la moins chère à faire.

Newtopian
la source
1

C'est le problème que j'appelle la confusion "objet / sujet" et il est assez répandu.

Les phrases ont généralement un sujet qui fait le verbe sur leur objet cible .

Maintenant, en ce qui concerne la programmation, la seule chose qui fait vraiment les choses est l'ordinateur. Ou pratiquement un processus, un fil ou une fibre. Les objets ne sont pas animés par défaut. Ils n'ont pas leurs propres threads en cours d'exécution, ils ne peuvent donc vraiment rien faire.

Cela signifie que les méthodes opèrent sur elles, elles sont la cible de l'action et non pas qui fait l'action. C'est pourquoi nous les appelons «objets» et non «sujets»!

Lorsque vous dites que File.closece n'est pas le fichier qui se ferme, c'est le thread en cours d'exécution qui ferme le fichier. Si vous dites Array.sort, le thread en cours d'exécution trie le tableau. Si vous dites HttpServer.sendRequest, le thread en cours d'exécution envoie la requête au serveur (et non l'inverse!). Dire de la même façon PunchingBag.punchsignifie que le fil en cours d'exécution frappe le sac.

Cela signifie que si vous voulez que le Boxerpoinçon puisse poinçonner, il doit s'agir d'une sous-classe d'un Threadafin qu'il puisse faire des choses comme des sacs de boxe dans sa fonction de fil.

Cependant, parfois, il est également logique de dire que le sac de boxe se frappe lui-même dans le cas où chaque objet a son propre thread, vous souhaiterez peut-être éviter les conditions de concurrence et implémenter des appels de méthode lors du passage du message: vous perforez le sac en lui envoyant le punchmessage, ce sont des threads lui-même vous renvoie alors le punch successfulmessage, mais ce n'est qu'un détail d'implémentation.

Calmarius
la source
0

Je suis d'accord que "punch" est un bon nom de méthode pour la classe Boxer, car (avec quelques ajustements) il pourrait être réutilisé contre d'autres objets. Il décrit également avec précision qu'un objet d'une classe effectue une action sur un autre objet. Cependant, je renommerais la méthode en "doPunch", pour démontrer plus clairement la relation.

Cependant, pour la classe PunchingBag, je trouve le nom de la méthode trop vague ou un peu imprécis de ce qui se passe dans la méthode. Quand je vois "punch", je pense que quelque chose frappe autre chose. Cependant, ici, l'objet PunchingBag réagit à un coup de poing provenant d'un objet (dans ce cas, un objet Boxer). Donc, je renommerais la méthode ici en "isPunched" pour illustrer que l'objet réagit à un coup de poing.

Cependant, c'est mon interprétation de la façon dont je nommerais les méthodes. Tout est une question de goût et de normes que vous suivez.

Marvon
la source
3
isPunchedest vraiment trompeur (plus ou moins, selon le schéma de nommage du framework).
Il est habituel que la méthode soit appliquée à cet objet, sur lequel il est appelé. Quel est le problème avec juste punch()?
Eh bien, je comprends parfaitement la nécessité de spécifier la direction de l'action, mais je pense qu'il y a quelque chose dans la POO et sa philosophie qui rend cela inutile. Une sorte d'abstraction liée à cette fameuse explication selon laquelle les objets «s'envoient des messages».
Clime
Si ce n'est pas évident à partir du nom de la méthode ce que fait la méthode, alors c'est un problème avec le nom. Ce n'est pas un problème avec OO ou quel que soit le paradigme utilisé. C'est pourquoi punch () sur un sac de boxe est incorrect dans le contexte que vous souhaitez utiliser. Qu'est-ce que cela signifie lorsque vous dites punch à un sac de boxe? C'est aussi pourquoi aucune philosophie ne peut supposer que quelque chose est inutile dans des situations où l'hypothèse crée une ambiguïté. Il y a des cas où les règles de base fonctionnent et des situations où elles ne fonctionnent pas. Si les règles empiriques fonctionnaient toujours, alors elles seraient appelées règles (sans le «pouce»).
Dunk
-2

hmmmm. Je remets en question le sac de boxe en tant que classe, parce que vous ne vous souciez pas vraiment du sac de boxe - vous vous souciez de l'impact et de la force du poing des boxeurs. les méthodes doivent donc porter sur tout ce qui mesure et rapporte l'impact du coup de poing. même si cela vient du «sac de boxe», la dénomination devrait quand même révéler la responsabilité - comme punchImpactMeter etc.

cartalot
la source
-3

Le boxeur frappe le punchingbag -> boxer.punch

Le punchingbag est frappé par le boxeur -> punchingbag.get_punch

P3Dr0
la source
3
cela ne semble pas offrir quoi que ce soit de substantiel par rapport aux points soulevés et expliqués dans les 6 réponses précédentes
gnat