My kemenworld…

To mock or not to mock…

Clases y estructuras bien formadas, Parte I

Posted by kementeus en febrero 13, 2008

Bueno, tenía buen tiempo de no escribir algo dividido en varias partes, creo que ya es hora :P. Muchos de nosotros ya somos más que familiares con los conceptos de clases y estructuras en .Net, muchos conocemos más que de memoria lo que nos suelen decir en las clases de «polimorfismo, herencia y encapsulamiento» (ojo, esos no son los tres principios básicos de POO, pero de eso hablaremos en otro post). Otros ya nos familiarizamos con todas las formas de herencia existente y a eso le sumamos el poder de los modificadores de herencia (sino revisense https://kementeus.wordpress.com/2007/08/28/clases-y-herencia-en-c-new-virtual-y-override/ y de paso visiten https://kementeus.wordpress.com/2008/01/25/clases-y-constructores-estticos/), bien, trataré de llevarlos un poco más allá de esos conceptos en esta corta serie de post sobre clases bien formadas, espero les sea útil a mas de alguien 😛

Comenzaremos con una clase sencilla, definamos un simple punto en un espacio bidimensional:

   1: namespace DegreeExample
   2: {
   3:     public class Point
   4:     {
   5:         private int x = 0;
   6:         private int y = 0;
   7:  
   8:         public Point() {}
   9:  
  10:         public Point(int x, int y)
  11:         {
  12:             X = x;
  13:             Y = y;
  14:         }
  15:  
  16:         public int X
  17:         {
  18:             get { return x; }
  19:             set { x = value; }
  20:         }
  21:  
  22:         public int Y
  23:         {
  24:             get { return y; }
  25:             set { y = value; }
  26:         }
  27:     }
  28: }

Si, ya se que más de alguien me dirá que esto sería representado mejor en una estructura, pero por simplicidad digamos que es mas bonito una clase :P. Bien, la mayoría de nosotros nos quedaríamos felices y contentos con nuestra clase para representar puntos, pero ahora imagínense una colección ordenada de puntos en el espacio, recordaremos que para ordenar colecciones (usando el método Sort de List por ejemplo) necesitamos implementar una forma estandar de buscar igualdad en la framework, más de alguien dirá «perfecto, hagamos override de Equals -un método contenido en Object-«, bien, porque no lo hacemos indirectamente, usando el equivalente preferido en la Framework 2.0, IEquatable<T>:

   1: using System;
   2:  
   3: namespace DegreeExample
   4: {
   5:     public class Point : IEquatable<Point>
   6:     {
   7:         private int x = 0;
   8:         private int y = 0;
   9:  
  10:         public Point() {}
  11:  
  12:         public Point(int x, int y)
  13:         {
  14:             X = x;
  15:             Y = y;
  16:         }
  17:  
  18:         public int X
  19:         {
  20:             get { return x; }
  21:             set { x = value; }
  22:         }
  23:  
  24:         public int Y
  25:         {
  26:             get { return y; }
  27:             set { y = value; }
  28:         }
  29:  
  30:         #region IEquatable<Point> Members
  31:  
  32:         public bool Equals(Point other)
  33:         {
  34:             return (other == null) ? false : (other.x.Equals(x) && other.y.Equals(y));
  35:         }
  36:  
  37:         #endregion
  38:     }
  39: }

Bien, con eso resolvemos el problema de igualdad, ahora podemos comparar si el punto p1 es igual al punto p2, probemos si eso es cierto:

   1: using System;
   2:  
   3: namespace DegreeExample
   4: {
   5:     internal class Program
   6:     {
   7:         private static void Main(string[] args)
   8:         {
   9:             Point p1 = new Point(1, 1);
  10:             Point p2 = new Point(2, 1);
  11:             Point p3 = new Point(1, 1);
  12:  
  13:             Console.WriteLine(p1.Equals(p2));
  14:             Console.WriteLine(p1.Equals(p3));
  15:  
  16:             Console.ReadLine();
  17:         }
  18:     }
  19: }

Operadores de igualdad

Claro, más de alguien me dirá: «no es como que más cómodo usar == en vez de Equals? imho claro esta!», bien, no se preocupen, es donde entra el overload de operadores, veamos como se implementaría en nuestra clase punto:

   1: using System;
   2:  
   3: namespace DegreeExample
   4: {
   5:     public class Point : IEquatable<Point>
   6:     {
   7:         private int x = 0;
   8:         private int y = 0;
   9:  
  10:         public Point() {}
  11:  
  12:         public Point(int x, int y)
  13:         {
  14:             X = x;
  15:             Y = y;
  16:         }
  17:  
  18:         public int X
  19:         {
  20:             get { return x; }
  21:             set { x = value; }
  22:         }
  23:  
  24:         public int Y
  25:         {
  26:             get { return y; }
  27:             set { y = value; }
  28:         }
  29:  
  30:         #region IEquatable<Point> Members
  31:  
  32:         public bool Equals(Point other)
  33:         {
  34:             return (other == null) ? false : (other.x.Equals(x) && other.y.Equals(y));
  35:         }
  36:  
  37:         #endregion
  38:  
  39:         public static bool operator ==(Point p1, Point p2)
  40:         {
  41:             return p1.Equals(p2);
  42:         }
  43:  
  44:         public static bool operator !=(Point p1, Point p2)
  45:         {
  46:             return !(p1 == p2);
  47:         }
  48:     }
  49: }

Como se daran cuenta es sumamente fácil implementar los operadores de igualdad en la .Net Framework, noten que al igual que implementé el operador de igualdad (==) también lo hice con el de «desigualdad» (!=) esto es importante ya que nos evita dolores de cabeza a la hora de que alguien más quiera usarlo.

Unicidad y Hashcode

Object tiene un método bastante usado internamente llamado Hashcode, sirve internamente para probar la igualdad de la instancia de una clase, devuelve un entero indicando el «estado» actual del objeto, el hashcode es ampliamente utilizado en colecciones para indicar si el objeto en cuestion se encuentra ya en la colección o no, una forma fácil de generar el HashCode de nuestro objeto punto sería transformar las coordenadas en texto y a su vez generar el Hashcode de ese texto, digamos que una coordenada x, y hace único al punto, bien tomando esto en cuenta digamos que hacemos lo siguiente:

   1: using System;
   2:  
   3: namespace DegreeExample
   4: {
   5:     public class Point : IEquatable<Point>
   6:     {
   7:         private int x = 0;
   8:         private int y = 0;
   9:  
  10:         public Point() {}
  11:  
  12:         public Point(int x, int y)
  13:         {
  14:             X = x;
  15:             Y = y;
  16:         }
  17:  
  18:         public int X
  19:         {
  20:             get { return x; }
  21:             set { x = value; }
  22:         }
  23:  
  24:         public int Y
  25:         {
  26:             get { return y; }
  27:             set { y = value; }
  28:         }
  29:  
  30:         #region IEquatable<Point> Members
  31:  
  32:         public bool Equals(Point other)
  33:         {
  34:             return (other == null) ? false : (other.x.Equals(x) && other.y.Equals(y));
  35:         }
  36:  
  37:         #endregion
  38:  
  39:         public static bool operator ==(Point p1, Point p2)
  40:         {
  41:             return p1.Equals(p2);
  42:         }
  43:  
  44:         public static bool operator !=(Point p1, Point p2)
  45:         {
  46:             return !(p1 == p2);
  47:         }
  48:  
  49:         public override int GetHashCode()
  50:         {
  51:             return String.Format("{0}|{1}", x, y).GetHashCode();
  52:         }
  53:     }
  54: }

Bien, eso es todo por hoy, dejaremos para otra entrega los siguientes aspectos que nos hacen falta, espero sus comentarios y como siempre perdonen mi horrible ortografía, saludos a todos!!!

UPDATE: https://kementeus.wordpress.com/2008/02/24/clases-y-estructuras-bien-formadas-parte-ii/

2 respuestas to “Clases y estructuras bien formadas, Parte I”

  1. GM said

    Me queda una duda con respecto a la sobre carga de operadores, tengo entendido que Visual Basic no tiene la capacidad de implementar sobrecargas, por lo tanto no es muy recomendado por compatibilidad entre lenguajes su uso.

    Saludos.

  2. kementeus said

    @GM: No precisamente, depende mucho de la versión con que trabajas. En VB.net 2005 por ejemplo, si existe soporte para sobrecarga de operadores, he aquí el link: http://msdn2.microsoft.com/en-us/library/ms379613.aspx

    Saludos!

Deja un comentario