Principios de OOP y nombres de métodos

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)

No hay dudas de que punches un buen nombre de método en el caso de un boxeador. ¿Pero el nombre punchtambién es bueno para el método de saco de boxeo? En ambos casos me refiero a golpe como un comando (es decir, golpear).

clima
fuente

Respuestas:

23

Una buena regla general es que los nombres de los métodos deben ser verbos o predicados de modo que el objeto sobre el que los invoque ( selfen la convención estándar de Python, thisen la mayoría de los otros idiomas) se convierta en el sujeto.

Según esta regla, file.closees un poco incorrecto, a menos que vaya con el modelo mental de que el archivo se cierra solo, o que el fileobjeto no representa el archivo en sí, sino más bien un identificador de archivo o algún tipo de objeto proxy.

Sin embargo, un saco de boxeo nunca se perfora solo, por lo que punchingBag.punch()está mal de cualquier manera. be_punched()es técnicamente correcto, pero feo. receive_punch()podría funcionar, o handle_punch(). Otro enfoque, bastante popular en JavaScript, es tratar tales llamadas a métodos como eventos, y la convención debe ir con el nombre del evento, con el prefijo 'on', para que sea on_punched()o on_hit(). Alternativamente, podría adoptar la convención de que los participios pasados ​​indican voz pasiva, y según esa convención, el nombre del método sería justo punched().

Otro aspecto a considerar es si el saco de boxeo sabe realmente qué lo golpeó: ¿hace alguna diferencia si lo golpeas, lo golpeas con un palo o te topas con un camión? Si es así, ¿cuál es la diferencia? ¿Puedes reducir la diferencia a una discusión, o necesitas diferentes métodos para diferentes tipos de castigo recibido? Un método único con un parámetro genérico es probablemente la solución más elegante, porque mantiene el grado de acoplamiento bajo, y dicho método no debería llamarse punched()o handle_punch(), sino más bien algo más genérico receive_hit(). Con tal método en su lugar, puede implementar todo tipo de actores que pueden golpear sacos de arena, sin cambiar el saco de arena en sí.

tdammers
fuente
44
@Artur: sí y no. Los archivos pueden (conceptualmente hablando) cerrarse cuando se les pide; las matrices pueden clasificarse a sí mismas; pero los sacos de boxeo no se perforan a sí mismos.
tdammers
2
De acuerdo, si nuestro saco de boxeo golpea la pared a una velocidad increíble, ¿es el muro el que lo golpeó o fue el saco de boxeo el que se golpeó contra sí mismo?
1
@tdammers: Su sugerencia para generalizar también podría conducir a una interfaz llamada Hitable.
Jens Piegsa
2
@Artur: Creo que aquí es donde se rompe la suposición de OOP de que cada oración tiene un tema natural, y que esta idea es aplicable a la programación.
tdammers
1
Entonces la pregunta principal es. Si los archivos pueden cerrarse, los arreglos pueden clasificarse, etc., ¿por qué los sacos de arena no pueden perforarse? ¿Hay alguna diferencia real o es solo que en el primer caso, estamos acostumbrados y en el segundo, no lo estamos?
Clima
6

Creo que es una cuestión conceptual (cómo pensamos sobre el mundo). Está bien decir:

  • Mira, la puerta se está cerrando. door.close()
  • Wow, el papel se dobla solo. paper.fold()
  • ¡¿Que demonios?! Ese archivo en el escritorio acaba de cerrarse, no hay nadie cerca. file.close()

Es raro decir:

  • Ese saco de boxeo en el gimnasio acaba de golpearse. bag.punch()

Tendría que tener algo con lo que golpearse en primer lugar (por ejemplo, brazos). Probablemente dirías:

  • El saco de boxeo comenzó a moverse por sí mismo como si alguien lo hubiera golpeado. punching_bag.move()

Está bien que los objetos programáticos hagan cosas que normalmente otros hacen con ellos (en el "mundo real"). Pero supongo que siempre debería tener al menos algún sentido que la cosa se está haciendo a sí misma . Debería poder imaginarlo fácilmente sin oscurecerse (como en el caso de punching_bag).

clima
fuente
2

Es cuestión de gustos, creo. Punching bagEl punch()método es al menos consistente con file.close()o frame.move()en el sentido de experimentar acción sobre sí mismo. Mayor pregunta sería, ¿por qué Boxertiene punch(something)método en absoluto?


fuente
Me gusta tu punto sobre file.close (). A eso me refería. Quizás el boxeador tiene un método de golpe porque también hay un entrenador que entrena al boxeador. Bueno, de hecho, solo estaba tratando de dar un ejemplo de una acción (mensaje) pasando a través de varios objetos, siendo el último "objeto de una acción". Tengo un pequeño problema con list.append (4), account.deposit (50), file.close (), paper.fold () vs. boxer.punch (), dog.bark (), logger.log () etc. .
clima
Al pasar a través de varios objetos, hay 2 casos: utiliza el contexto vinculado (self) y no lo hace. Si lo hace, sus métodos deberían ser Coach.sayPunchToBoxer(), Boxer.punchNearestBag()y Bag.punch(). De lo contrario, debe adivinar lo que sucederá cada vez que llame Coach.punch(). La regla general es: si el objeto que experimenta acción no se especifica en el nombre del método, entonces el receptor es ese objeto.
Bueno, creo que esto también está bien: coach.say_punch (boxer, punching_bag), boxer.punch (punching_bag). es decir, el receptor no está en el nombre del método sino en los parámetros.
Clima
1
Claro, quise decir que el receptor de acción debería ser adivinable a partir de la declaración de llamada.
2

Tiene dos mensajes diferentes: uno para ordenar que un objeto golpee y otro para informar a un objeto que fue perforado. Tenga en cuenta que es probable que un objeto Boxer deba responder a ambos . De manera diferente . Esa es una muy buena razón para darles diferentes nombres.

Mi inclinación sería mantener punch(boxer, object, strength)y cambiar el nombre del método opuesto a punched. Podría llamarlo handle_puncho algo así, pero aún es ambiguo si se trata de manejar un comando de golpe o la notificación de que se golpeó.

usuario2313838
fuente
Un buen punto sobre Boxer que necesita tanto golpe como algo así como handle_punch (sería defenden este caso particular). Pero el saco de boxeo nunca será bidireccional como ese. Y ya existe este archivo .close () ...
clima
defendes un comando Es una posible acción que un objeto podría realizar en respuesta punched, pero no querrá que otros objetos invoquen defenddirectamente.
user2313838
2

Su enfoque eventualmente conducirá a un código muy acoplado.

Para resumir, Eric Lippert, idealmente aquí, querrás que el boxeador sea capaz de golpear muchas cosas. Tener el saco de boxeo como la firma de la función de boxeador implica que el boxeador se crea con un conocimiento inmediato de Todos (eso es punzante). Además, dar un puñetazo y recibir un puñetazo son dos cosas MUY diferentes, por lo tanto, no deberían compartir el mismo nombre.

Prefiero modelar esto como un Boxer que crea un golpe (otro objeto que contiene la fuerza, el alcance, la dirección, etc. del atributo del golpe).

Luego, tenga el saco de boxeo con un método como onPunch que recibe este objeto de punzón para calcular el efecto del punzón en sí mismo.

Teniendo esto en cuenta, el nombre de las cosas es muy importante. Debe ajustarse al modelo mental que tiene de la situación. Si se encuentra tratando de explicar cómo puede suceder algo que no tiene sentido a primera vista, o si le resulta más difícil nombrar algo, entonces quizás su modelo esté equivocado y deba cambiar.

Es difícil cambiar un modelo después de comenzar, la gente generalmente tiende a doblegar la realidad para que se ajuste al modelo. El problema con esto es que a medida que dobla las cosas para que quepan (como un saco de boxeo que puede perforar cosas) el mundo que está creando se vuelve cada vez más complejo y las interacciones se vuelven cada vez más difíciles de implementar. Eventualmente llegarás a un punto en el que agregar incluso lo más trivial se convierte en una pesadilla de cambios y errores. Esta deuda técnica conceptual puede tener un precio muy elevado, incluso si el costo inicial se percibía en ese momento como lo más barato.

Newtopian
fuente
1

Este es el problema que llamo confusión "objeto / sujeto" y es bastante frecuente.

Las oraciones generalmente tienen un sujeto que hace el verbo en su objeto objetivo .

Ahora con respecto a la programación, lo único que realmente hace las cosas es la computadora. O prácticamente un proceso, hilo o fibra. Los objetos no son animados por defecto. No tienen sus propios hilos en ejecución, por lo que realmente no pueden hacer nada.

Esto significa que los métodos operan en ellos, son el objetivo de la acción, no quién realiza la acción. ¡Es por eso que los llamamos "objetos", no "sujetos"!

Cuando dice File.closeque no se cierra el archivo, es el subproceso actual que cierra el archivo. Si dice Array.sort, el subproceso actual ordena la matriz. Si dice HttpServer.sendRequest, el subproceso actual en ejecución envía la solicitud al servidor (¡no al revés!). De manera similar, decir PunchingBag.punchsignifica que el hilo corriente actual golpea la bolsa.

Esto significa que si desea Boxerpoder perforar, entonces debe ser una subclase de una Threadpara que pueda hacer cosas como sacos de boxeo en su función de hilo.

Sin embargo, a veces también tiene sentido decir que el saco de boxeo se golpea solo en el caso de que cada objeto tenga su propio hilo, es posible que desee evitar las condiciones de carrera e implementar llamadas de método como mensaje que pasa: punchgolpea el saco enviándole el mensaje, son golpes de hilo a continuación, le devuelve el punch successfulmensaje, pero eso es solo un detalle de implementación.

Calmarius
fuente
0

Estoy de acuerdo en que "punch" es un buen nombre de método para la clase Boxer, ya que (con algunos ajustes) podría reutilizarse contra otros objetos. También describe con precisión que un objeto de una clase está realizando una acción sobre otro objeto. Sin embargo, cambiaría el nombre del método a "doPunch", para demostrar más claramente la relación.

Sin embargo, para la clase PunchingBag, encuentro que el nombre del método es demasiado vago o un poco impreciso de lo que está sucediendo en el método. Cuando veo "golpe", creo que algo está golpeando a otra cosa. Sin embargo, aquí, el objeto PunchingBag está reaccionando a un golpe de un objeto (en este caso, un objeto Boxer). Entonces, cambiaría el nombre del método aquí a "isPunched" para ilustrar que el objeto está reaccionando a un golpe.

Sin embargo, esta es mi interpretación de cómo nombraría los métodos. Todo es cuestión de gustos y qué estándares estás siguiendo.

Marvon
fuente
3
isPunchedes realmente engañoso (más o menos, dependiendo del esquema de nombres del marco).
Es habitual que el método se aplique a ese objeto, en el que se llama. ¿Qué hay de malo con solo punch()?
Bueno, entiendo totalmente la necesidad de especificar la dirección de la acción, pero creo que hay algo acerca de OOP y su filosofía que hace que esto sea innecesario. Algún tipo de abstracción relacionada con esa famosa explicación de que los objetos "se envían mensajes" entre sí.
Clima
Si no es obvio por el nombre del método lo que está haciendo el método, entonces es un problema con el nombre. No es un problema con OO o cualquier paradigma que se esté utilizando. Es por eso que punch () en un saco de boxeo está mal en cualquier contexto que desee usar. ¿Qué significa cuando dices golpe a un saco de boxeo? También es la razón por la cual no puede asumir, por ninguna filosofía, que algo es innecesario en situaciones donde la suposición crea ambigüedad. Hay casos en que las reglas generales funcionan y situaciones en las que no. Si las reglas generales siempre funcionaran, entonces se llamarían reglas (sin el "detalle").
Dunk
-2

hmmmm Estoy cuestionando el saco de boxeo como clase, porque realmente no te importa el saco de boxeo, te importa el impacto y la fuerza del puño de los Boxers. Por lo tanto, los métodos deben ser sobre lo que sea medir e informar el impacto del golpe. incluso si esto proviene del 'saco de boxeo', el nombre aún debe revelar la responsabilidad, como punchImpactMeter, etc.

cartalot
fuente
-3

El boxeador golpea el saco de boxeo -> boxer.punch

El boxeador golpea el saco de boxeo -> punchingbag.get_punch

P3Dr0
fuente
3
Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en 6 respuestas anteriores
mosquito