C#–Buenas prácticas en constructores

 ·  ☕ 11 min  ·  ✍️ eiximenis

    Nota: Este post ha sido importado de mi blog de geeks.ms. Es posible que algo no se vea del todo "correctamente". En cualquier caso puedes acceder a la versión original aquí

    Escribir el constructor de una clase es algo que parece trivial… A fin de cuentas, el constructor se encarga de construir un objeto, ¿no? Pero la realidad es que escribir constructores no es tan sencillo como parece. ¿Qué significa “construir” un objeto? Por supuesto cada clase tendrá sus propias necesidades, pero hay una serie de guías y buenas prácticas que nos pueden ayudar a tomar ciertas decisiones. A esto va dedicado este post.

    Guia 1: Haz lo mínimo posible en el constructor

    Sean cuales sean las necesidades de tu clase, haz que el constructor haga lo mínimo posible. Técnicamente un constructor no debería poder fallar casi nunca. De hecho el constructor debería limitarse a guardarse los parámetros necesarios y poca cosa más. Evita constructores que hagan demasiadas cosas. Veamos dos ejemplos contrapuestos. El primero la clase FileStream. El siguiente código:

    1. using (var fs = new FileStream(@"C:\\foo.txt", FileMode.Open, FileAccess.Read))
    2. {
    3. }

    Falla si no existe el fichero C:\foo.txt o si el usuario no tiene permisos, o si dicho fichero existe pero está bloqueado. El constructor intenta abrir el fichero, una operación que por un lado es potencialmente larga (el fichero puede estar en una carpeta remota) y por otra tiene muchas posibilidades de fallo. El constructor de FileStream hace demasiadas cosas. Hubiese sido mejor si hubiesen seguido otra aproximación. P. ej. la de SqlConnection. El siguiente código no falla nunca:

    1. using (var con = new SqlConnection("server=100.0.0.1;database=myDb;uid=myUser;password=myPass;"))
    2. {
    3. }

    Da absolutamente igual que el servidor exista o no, o que la base de datos exista, o que el usuario tenga permisos. El constructor no intenta establecer conexión alguna y delega operación en un método de instancia (Open). Eso tiene varias ventajas:

    1. Crear objetos SqlConnection es una tarea sencilla y asegura al desarrollador que el tiempo en hacerlo es corto y que los fallos son inexistentes.
      • El desarrollador puede retardar la llamada a la operación que puede fallar y/o tardar tiempo todo lo necesario.

        • Se puede proporcionar una versión asíncrona (la propia clase SqlConnection define OpenAsync), lo que no es posible si el constructor realizara esas operaciones (no hay constructores asíncronos).

    En resúmen FileStream debería proporcionar un método Open en lugar de intentar abrir el fichero desde el constructor. Al no hacerlo, implica que el desarrollador debe diferir la creación entera del objeto lo más tarde posible (en lugar de diferir solo la operación potencialmente costosa), lo que obliga a lidiar con posibles referencias null y/o usar otras técnicas como Lazy.

    Guía 2 –No llames métodos virtuales desde el constructor

    O ándate con ojo si lo haces. Eso es de hecho un warning del compilador, así que deberíamos prestarle atención. La razón es que, dada una clase A, que define un método virtual m() y una clase B que hereda de A y redefine el método virtual, cuando el constructor de A llame al método m(), el método llamado no tiene porque ser el método de la clase A, si no que puede ser el método de la clase derivada (B), que se ejecutará antes que el propio constructor de la clase derivada.

    Reconozco que explicado así puede costar de entender, así que mejor verlo con un ejemplo. Empecemos por la clase A:

    1. class A
    2. {
    3.     public A()
    4.     {
    5.         m();
    6.     }
    7.     protected virtual void m()
    8.     {
    9.     }
    10. }

    El constructor de A, llama al método virtual m(). En este caso no hace nada más, en un caso real, probablemente el constructor de A haría algo antes y algo después y se quiere dar la opción a las clases derivadas de personalizar parte del comportamiento. Da igual, no es relevante. Veamos ahora la clase B:

      Si quieres, puedes invitarme a un café xD

    eiximenis
    ESCRITO POR
    eiximenis
    Compulsive Developer