2009-06-11 9 views
14

Esta pregunta está relacionada con C#, pero también puede aplicarse a otros idiomas. Tengo una reserva contra el uso de código como el siguiente:C#: ¿Llamar explícitamente a un controlador de eventos es realmente "algo bueno que hacer"?

using System.Windows.Forms; 

class MyForm : Form 
{ 
    private Timer myTimer; 
    private Button myButton; 

    public MyForm() 
    { 
     // Initialize the components, etc. 

     myTimer.Tick += new EventHandler(myTimer_Tick); 
     myButton.Click += new EventHandler(myButton_Click); 

     myTimer.Start(); 
    } 

    private void myTimer_Tick(object sender, EventArgs eventArgs) 
    { 
     myTimer.Stop(); 
     // also, I see a lot of usage of 
     // Timer.Enabled = true/false instead of -^ 
     myButton_Click(this, ea /* or event EventArgs.Empty, or null */); 
     return; 
    } 

    private void myButton_Click(object sender, EventArgs eventArgs) 
    { 
     // do a lot of stuff, with lots of logic that doesn't even use the 
     // state of the eventArgs 
     return; 
    } 
} 

Estoy solo, en la que el estilo anterior es una manía mía? ¿Hay otros que disfrutan la claridad de separar el manejo de eventos de la carga de trabajo de las funciones, o incluso separar las rutinas complejas en funciones separadas?

¿Hay incluso un estilo aceptado? Siento que cualquier expresividad y flexibilidad que el manejo de eventos en C# tiene se puede perder con estilos como este. Siento que si tiene un método que significa "se ha hecho clic en un botón", solo se debe llamar cuando se hace clic en un botón.

Para aquellos que escriben de esta manera, diría: si insistes en tener un método EventHandler para manejar tu temporizador y tu botón, llamalo de otra manera que no sea button_Click - quizás "handleUserEvent(object sender, EventArgs eventArgs)".

Realmente, sin embargo, la pregunta es, ¿hay alguna guía de estilo ampliamente utilizada que apoye o desaliente el uso como el anterior?

+1

Sí, esta no es una buena práctica, en mi opinión tampoco. Su idea de 'HandleUserEvent' es probablemente una buena manera de evitarlo, aunque probablemente pueda nombrarlo mejor en la mayoría de las situaciones. – Noldorin

Respuesta

7

Estoy de acuerdo con Rex M's answer, pero daría un paso más. Si está utilizando el patrón MVC (o algo similar), la vista delegaría el clic del botón al controlador. Por supuesto, los métodos de los controladores pueden ser llamados desde cualquier parte de su clase, por ejemplo, desde la devolución de llamada de su temporizador.

tanto, de vuelta a su código original:

using System.Windows.Forms; 

class MyForm : Form 
{ 
    private Timer myTimer; 
    private Button myButton; 

    private MyController myController; 

    public MyForm() 
    { 
     // ... 
     // Initialize the components, etc. 
     // ... 

     myTimer.Tick += new EventHandler(myTimer_Tick); 
     myButton.Click += new EventHandler(myButton_Click); 

     myTimer.Start(); 
    } 

    private void myTimer_Tick(object sender, EventArgs eventArgs) 
    { 
     myTimer.Stop(); 
     myController.SomeMethod() 
    } 

    private void myButton_Click(object sender, EventArgs eventArgs) 
    { 
     // All the stuff done here will likely be moved 
     // into MyController.SomeMethod() 
     myController.SomeMethod(); 
    } 
} 

Una ventaja de utilizar MVC es el desacoplamiento del controlador de la vista. El controlador ahora se puede usar fácilmente en múltiples tipos de vistas y es más fácil mantener las GUI existentes, ya que contienen muy poca lógica de aplicación.

EDIT: Añadido en respuesta a los comentarios de la OP

Los principios de diseño fundamentales de la ingeniería de software charla sobre acoplamiento y cohesión. Es importante destacar que nos esforzamos por minimizar el acoplamiento entre los componentes a la vez que maximizamos la cohesión ya que esto lleva a un sistema más modular y mantenible. Patrones como MVC y principios como Open/Closed Principal se basan en estos fundamentos, proporcionando patrones más tangibles de implementación para que el desarrollador los siga.

Por lo tanto, cualquier persona que escribe código como se ve en la publicación original no ha entendido los fundamentos del diseño del software y necesita desarrollar sus habilidades considerablemente. El OP debe ser elogiado por identificar este "olor a código" e intentar entender por qué no está del todo bien.

Algunas referencias relevantes:

+0

Supongo que la respuesta a mi pregunta es, sí, hay otros por los que este estilo se ve como incorrecto. ¿En cuanto a dónde están los estándares? Supongo que sería "dependiente". Dependiendo de si está siguiendo el diseño de MVC, por ejemplo. Alternativamente, si usa el estilo de código como en la pregunta publicada, ¿por qué lo hace? ¿O incluso publicarlo como respuestas a preguntas, como si lo respaldaras? – maxwellb

+0

@mpbloch: He agregado algunos comentarios adicionales a mi respuesta. La respuesta a su pregunta original es "Sí, el código que publicó es de mala calidad". Y también, "No, no estás solo". Sugiero leer algunos buenos libros de diseño de software y patrones de diseño (el grupo de cuatro libros, "Patrones de diseño" sería un gran comienzo) para construir algunas garantías para utilizar contra los detractores con los que trabajas. Tus intuiciones son correctas, ¡ahora ve a demostrarlo y haz un progreso real! –

+0

@Daniel: gracias por la actualización y la recomendación del libro. Seguramente lo agregaré a mi estantería de préstamos para decir: "Aquí, lean esto" ;-) (Como dar un olor a niño maloliente [@code smell]) – maxwellb

0

Las cosas especiales acerca de los eventos en C# (y el framework .Net en general es el delegado, que es el equivalente C/C++ de un puntero a función. El método adjunto al evento no es especial de ninguna manera y debería ser llamable desde cualquier lugar.

Actualización: quizás debería haber sido más detallado, pero pensé que mi uso de "debería" en lugar de "puede" o "puede" sería suficiente. Es mi afirmación que los controladores de eventos deberían ser llamado cuando se necesita la funcionalidad que implementan, en lugar de convertirlos en envoltorios de métodos que "hacen el trabajo", cuanto menos llamadas de método tenga en la pila, mejor será su capacidad, especialmente con las implicaciones de rendimiento del manejo de excepciones de .Net.

+0

sí, pero la pregunta es si es una buena práctica llamar al controlador desde otro código. – Jimmy

+0

se trata de una decisión de diseño, no se trata de si es físicamente posible – benPearce

+0

Si una aplicación C# necesita preocuparse por la carga de la pila, es probable que alguien elija el idioma equivocado en primer lugar. – Trap

23

Esto definitivamente no es una "preferencia personal". Existe un enfoque claro y bien entendido de cómo escribir código que está bien estructurado, es fácil de mantener, reutilizable y comprensible. Cada método en su código debe encapsular una sola pieza de funcionalidad reutilizable. La estructura de su código debe ser:

void ButtonClickEventHandler(...) 
{ 
    UserData userData = //determine user data from event data 
    DoUserThing(userData); 
} 

void DoUserThing(UserData userData) 
{ 
    //do stuff 
} 

void SomeOtherMethod() 
{ 
    UserData userData = //get userdata from some other source 
    DoUserThing(userData); 
} 

(Este es un ejemplo muy floja En una aplicación adecuada debería ser todo lo separated into different classes by concern..)

5

myButton.PerformClick() es probablemente un poco más agradable, si no es necesario pasar eventargs. Algunas veces solo quieres simular un clic.

Pero sí, estoy de acuerdo en que es mejor trasladar el código real a otra función. Prefiero que mis manejadores de eventos sean muy simples, simplemente conecte la IU a la lógica, que está en otro lado.

Luego puede reorganizar y rediseñar su interfaz de usuario sin preocuparse demasiado por la lógica.

1

Este código aumenta las posibilidades de problemas si otro codificador funciona en el método myButton_Click.

¿Qué sucede si entro para ajustar la implementación del controlador myButton.Click?Podría suponer que el remitente del objeto es un botón, y tratar de fundido:

Button b = (Button)sender; 

no tengo conocimiento sin leer el resto de la implementación de la clase que no siempre estoy recibiendo un botón como remitente.

Así que mi punto es: -1 para el mantenimiento, debido a la ruptura de las suposiciones de qué objetos se pasarán como parámetros myButton_Click.

1

La respuesta corta es que ¿por qué simular un clic de botón llamando directamente al controlador? Si quiere conectar ambos métodos al mismo evento, simplemente lo conectará. Los controladores de eventos son delegados de multidifusión, lo que significa que puede agregar más de uno. Conectar un evento más de una vez es totalmente aceptable.

myTimer.Tick += myTimer_Tick; 
    myTimer.Tick += myButton_Click; 

    myButton.Click += myButton_Click; 

Sea o no esto es un WTF es una llamada de ingeniería que no podemos hacer a partir de un fragmento de código corto. Sin embargo, según sus comentarios, huele como una WTF. Los formularios o cualquier UI nunca deben manejar la lógica de negocios. Necesitan ser conscientes de la lógica del negocio hasta cierto punto (como en la validación), pero no encapsulan/imponen la lógica ellos mismos.

Yendo más lejos, seguir algunas prácticas simples como refactorizaciones básicas y utilizar un enfoque estratificado (de n niveles) para el software le llevará un largo camino, y se dará cuenta en el camino que el código que presentó huele mal.

Eventualmente se encontrará con algunos patrones de alto nivel como MVC (modelo-vista-controlador) y MVP (modelo-vista-presentador) que van un paso más allá de la simple estratificación. Si los sigues, obtienes una buena separación de preocupaciones.

Estoy de acuerdo con la respuesta aceptada, pero voy directamente a 'Use MVC', aquí hay un código que no ilustra MVC, sin explicar por qué es un pequeño culto a la carga para mí.

Cuestiones relacionadas