Fondo
Actualmente tengo una situación en la que tengo un objeto que es transmitido y recibido por un dispositivo. Este mensaje tiene varias construcciones, como sigue:
public void ReverseData()
public void ScheduleTransmission()
El ScheduleTransmission
método debe llamar al ReverseData
método siempre que se llame. Sin embargo, hay momentos en los que tendré que llamar ReverseData
externamente (y debería agregarlos fuera del espacio de nombres por completo ) desde donde se instancia el objeto en la aplicación.
En cuanto a la "recepción" quiero decir que ReverseData
se llamará externamente en un object_received
controlador de eventos para anular la inversión de los datos.
Pregunta
¿Es generalmente aceptable que un objeto llame a sus propios métodos públicos?
c#
design-patterns
object-oriented
methods
Fisgonear
fuente
fuente
Respuestas:
Yo diría que no solo es aceptable, sino que se recomienda especialmente si planea permitir extensiones. Para admitir extensiones de la clase en C #, deberá marcar el método como virtual según los comentarios a continuación. Sin embargo, es posible que desee documentar esto, para que alguien no se sorprenda cuando anule ReverseData () y cambie la forma en que funciona ScheduleTransmission ().
Realmente se reduce al diseño de la clase. ReverseData () suena como un comportamiento fundamental de su clase. Si necesita usar este comportamiento en otros lugares, probablemente no quiera tener otras versiones de él. Solo debe tener cuidado de no dejar que los detalles específicos de ScheduleTransmission () se filtren en ReverseData (). Eso creará problemas. Pero como ya está usando esto fuera de la clase, probablemente ya lo haya pensado detenidamente.
fuente
ReverseData()
sea abstracto, no importa lo que haga en la clase secundaria, siempre se llamará al método original.virtual
... Y si está anulando el método, entonces, por definición, debe servirtual
oabstract
.virtual
también funciona. En todos los casos, la firma, según OP, espublic void ReverseData()
, por lo que la parte de la respuesta sobre la anulación de cosas es ligeramente engañosa.abstract
? Busqué respuestas enabstract
vsvirtual
y no vi lo mencionado: más bien abstracto solo significa que no hay una versión dada en esta base.Absolutamente.
La visibilidad de un método tiene el único propósito de permitir o denegar el acceso a un método fuera de la clase o dentro de una clase secundaria; Los métodos públicos, protegidos y privados siempre se pueden llamar dentro de la propia clase.
No hay nada de malo en llamar a métodos públicos. La ilustración en su pregunta es el ejemplo perfecto de una situación en la que tener dos métodos públicos es exactamente lo que debe hacer.
Sin embargo, puede estar atento a los siguientes patrones:
Un método
Hello()
llamaWorld(string, int, int)
así:Evita este patrón. En su lugar, use argumentos opcionales . Los argumentos opcionales facilitan la detección: la persona que llama que escribe
Hello(
no necesariamente sabrá que existe un método que hace posible llamar al método con valores predeterminados. Los argumentos opcionales también se documentan automáticamente.World()
no muestra al llamante cuáles son los valores predeterminados reales.Un método
Hello(ComplexEntity)
llamaWorld(string, int, int)
así:En su lugar, use sobrecargas . La misma razón: mejor capacidad de descubrimiento. La persona que llama puede ver de inmediato todas las sobrecargas a través de IntelliSense y elegir la correcta.
Un método simplemente llama a otros métodos públicos sin agregar ningún valor sustancial.
Pensar dos veces. ¿Realmente necesitas este método? ¿O debería eliminarlo y dejar que la persona que llama invoque los diferentes métodos? Una pista: si el nombre del método no parece correcto o es difícil de encontrar, probablemente debería eliminarlo.
Un método valida la entrada y luego llama al otro método:
En cambio,
World
debe validar sus parámetros en sí mismo o ser privado.fuente
Si algo es público, cualquier sistema puede llamarlo en cualquier momento. ¡No hay razón para que no puedas ser uno de esos sistemas también!
En bibliotecas altamente optimizadas, desea copiar el idioma presentado
java.util.ArrayList#ensureCapacity(int)
.asegurarCapacidad
ensureCapacity
es públicoensureCapacity
tiene todos los límites necesarios de verificación y valores predeterminados, etc.ensureCapacity
llamadasensureExplicitCapacity
sureCapacityInternal
ensureCapacityInternal
es privadoensureCapacityInternal
tiene una comprobación mínima de errores porque todas las entradas provienen del interior de la claseensureCapacityInternal
TAMBIÉN llamaensureExplicitCapacity
sureExplicitCapacity
ensureExplicitCapacity
TAMBIÉN es privadoensureExplicitCapacity
no tiene comprobación de erroresensureExplicitCapacity
hace trabajo realensureExplicitCapacity
no se llama desde ninguna parte excepto porensureCapacity
yensureCapacityInternal
De esta manera, el código en el que confía obtiene acceso privilegiado (¡y más rápido!) Porque sabe que sus entradas son buenas. El código en el que no confía pasa por cierto rigor para verificar su integridad y bombardear, proporcionar valores predeterminados o manejar entradas incorrectas. Ambos canalizan al lugar que realmente funciona.
Sin embargo, esto se usa en ArrayList, una de las clases más utilizadas en el JDK. Es muy posible que su caso no requiera ese nivel de complejidad y rigor. En pocas palabras, si todas las
ensureCapacityInternal
llamadas se reemplazaran porensureCapacity
llamadas, el rendimiento aún sería muy, muy bueno. Esta es una microoptimización probablemente solo realizada después de una extensa consideración.fuente
Ya se han dado varias buenas respuestas, y también estaría de acuerdo con ellas en que sí, un objeto puede llamar a sus métodos públicos desde sus otros métodos. Sin embargo, hay una leve advertencia de diseño que deberá tener en cuenta.
Los métodos públicos generalmente tienen un contrato de "tomar el objeto en un estado consistente, hacer algo sensato, dejar el objeto en un estado consistente (posiblemente diferente)". Aquí, "coherente" puede significar, por ejemplo, que el
Length
de aList<T>
no es mayor que suCapacity
y que no hace referencia a los elementos en los índices de0
aLength-1
.Pero dentro de los métodos del objeto, el objeto puede estar en un estado inconsistente, por lo que cuando llama a uno de sus métodos públicos, puede hacer algo muy incorrecto, porque no fue escrito con esa posibilidad en mente. Entonces, si planea llamar a sus métodos públicos desde sus otros métodos, asegúrese de que su contrato sea "tomar el objeto en algún estado, hacer algo sensato, dejar el objeto en un (posiblemente diferente) (tal vez inconsistente, pero solo si la inicial estado era inconsistente) estado ".
fuente
Otro ejemplo cuando llamar al método público dentro de otro método público es totalmente correcto es un enfoque CanExecute / Execute. Lo uso cuando necesito validación y preservación invariante .
Pero en general siempre soy cauteloso al respecto. Si un método
a()
se llama método internob()
, significa que ese métodoa()
es un detalle de implementación deb()
. Muy a menudo indica que pertenecen a diferentes niveles de abstracción. Y el hecho de que ambos sean públicos me hace preguntarme si es una violación del principio de responsabilidad única o no.fuente
Lo siento, pero voy a tener que estar en desacuerdo con la mayoría de las otras respuestas 'sí, puedes' y decir eso:
Desalentaría una clase que llama un método público de otro
Hay un par de posibles problemas con esta práctica.
1: bucle infinito en clase hertited
Entonces, su clase base llama al método1 del método2, pero luego usted u otra persona hereda de él y oculta el método1 con un nuevo método que llama al método2.
2: Eventos, registro, etc.
por ejemplo, tengo un método Add1 que dispara un evento '¡1 agregado!' Probablemente no quiero que el método Add10 genere ese evento, escriba en un registro o lo que sea, diez veces.
3: roscado y otros puntos muertos
Por ejemplo, InsertComplexData abre una conexión db, inicia una transacción, bloquea una tabla, luego llama a InsertSimpleData, con abre una conexión, inicia una transacción, espera a que la tabla se desbloquee ...
Estoy seguro de que hay más razones, una de las otras respuestas tocó 'editas el método1 y te sorprende que el método2 comience a comportarse de manera diferente'
En general, si tiene dos métodos públicos que comparten código, es mejor hacer que ambos llamen a un método privado en lugar de que uno llame al otro.
Editar ----
Vamos a ampliar el caso específico en el OP.
no tenemos muchos detalles, pero sabemos que ReverseData es llamado por un controlador de eventos de algún tipo, así como por el método ScheduleTransmission.
Supongo que los datos inversos también cambian el estado interno del objeto
Dado este caso, pensaría que la seguridad del hilo sería importante y, por lo tanto, se aplica mi tercera objeción a la práctica.
Para hacer que el hilo ReverseData sea seguro, puede agregar un bloqueo. Pero si ScheduleTransmission también necesita ser seguro para subprocesos, querrá compartir el mismo bloqueo.
La forma más fácil de hacerlo es mover el código ReverseData a un método privado y hacer que ambos métodos públicos lo llamen. Luego puede colocar la declaración de bloqueo en los Métodos públicos y compartir un objeto de bloqueo.
Obviamente puedes argumentar "¡eso nunca sucederá!" o "Podría programar el bloqueo de otra manera", pero el punto sobre las buenas prácticas de codificación es estructurar bien su código en primer lugar.
En términos académicos, diría que esto viola la L en sólido. Los métodos públicos son más que solo accesibles públicamente. También son modificables por sus herederos. Su código debe cerrarse para su modificación, lo que significa que debe pensar qué trabajo realiza en los métodos públicos y protegidos.
Aquí hay otro: también violas potencialmente DDD. Si su objeto es un objeto de dominio, sus métodos públicos deben ser términos de dominio que significan algo para la empresa. En este caso, es muy poco probable que 'comprar una docena de huevos' sea lo mismo que 'comprar 1 huevo 12 veces', incluso si comienza de esa manera.
fuente