My kemenworld…

To mock or not to mock…

Clases y estructuras bien formadas, Parte II

Posted by kementeus en febrero 24, 2008

En la última entrega estubimos hablando de igualdad entre clases y como afectaba nuestra concepción de objetos, usamos para eso una clase simple con una coordenada en el espacio. Hoy modificaremos un poco la clase y usaremos una clase para identificar un número imaginario, recuerdan? aquellos números (también llamados “complejos”) donde existe una raíz imaginaria o compleja que representa o explica que su potencia de dos es igual a menos 1 (para mayor info tenemos a nuestra amiga Wikipedia)😛

Nuevamente les recalco que las matemáticas no fueron mi fuerte, así que pido perdon a aquellos expertos que encuentren errores conceptuales en el artículo😛, bueno, comencemos donde quedamos la última vez.

   1: using System;
   2:  
   3: namespace BlogExamples.WellFormedStructures
   4: {
   5:     public class ComplexNumber : IEquatable<ComplexNumber>
   6:     {
   7:         private readonly int _complexPart;
   8:         private readonly int _realPart;
   9:  
  10:         public ComplexNumber(int realPart, int complexPart)
  11:         {
  12:             _realPart = realPart;
  13:             _complexPart = complexPart;
  14:         }
  15:  
  16:         public int RealPart
  17:         {
  18:             get { return _realPart; }
  19:         }
  20:  
  21:         public int ComplexPart
  22:         {
  23:             get { return _complexPart; }
  24:         }
  25:  
  26:         public bool Equals(ComplexNumber other)
  27:         {
  28:             return (_complexPart.Equals(other.ComplexPart) && _realPart.Equals(other.RealPart));
  29:         }
  30:  
  31:         public static bool operator ==(ComplexNumber complex1, ComplexNumber complex2)
  32:         {
  33:             return complex1.Equals(complex2);
  34:         }
  35:  
  36:         public static bool operator !=(ComplexNumber complex1, ComplexNumber complex2)
  37:         {
  38:             return !(complex1 == complex2);
  39:         }
  40:  
  41:         public override int GetHashCode()
  42:         {
  43:             return string.Format("{0}|{1}", _realPart, _complexPart).GetHashCode();
  44:         }
  45:     }
  46: }

Bien, seguimos bajo la premisa que todo esto es solo como ejemplo verdad? si, si, si, más de alguien me dirá que hay una “mejor” forma de hacerlo pero lo único que quiero es explicar esto, ok?. Continuando con el ejemplo una simple unidad de prueba que comprobaría si todo funciona bien sería algo así:

   1: using NUnit.Framework;
   2: using NUnit.Framework.SyntaxHelpers;
   3:  
   4: namespace BlogExamples.WellFormedStructures.Fixtures
   5: {
   6:     [TestFixture]
   7:     public class ComplexNumberFixture
   8:     {
   9:         [Test]
  10:         public void TestGetPropertiesCorrectly()
  11:         {
  12:             ComplexNumber complex = new ComplexNumber(1, 0);
  13:             Assert.That(complex.RealPart, Is.EqualTo(1));
  14:             Assert.That(complex.ComplexPart, Is.EqualTo(0));
  15:         }
  16:  
  17:         [Test]
  18:         public void TestEqualsWorks()
  19:         {
  20:             ComplexNumber complex01 = new ComplexNumber(1, 0);
  21:             ComplexNumber complex02 = new ComplexNumber(0, 1);
  22:             ComplexNumber complex03 = new ComplexNumber(1, 0);
  23:  
  24:             Assert.That(complex01.Equals(complex02), Is.False);
  25:             Assert.That(complex01.Equals(complex03), Is.True);
  26:         }
  27:  
  28:         [Test]
  29:         public void TestEqualOperatorWorks()
  30:         {
  31:             ComplexNumber complex01 = new ComplexNumber(1, 0);
  32:             ComplexNumber complex02 = new ComplexNumber(0, 1);
  33:             ComplexNumber complex03 = new ComplexNumber(1, 0);
  34:  
  35:             Assert.That(complex01 == complex02, Is.False);
  36:             Assert.That(complex01 == complex03, Is.True);
  37:         }
  38:  
  39:         [Test]
  40:         public void TestNotEqualOperatorWorks()
  41:         {
  42:             ComplexNumber complex01 = new ComplexNumber(1, 0);
  43:             ComplexNumber complex02 = new ComplexNumber(0, 1);
  44:             ComplexNumber complex03 = new ComplexNumber(1, 0);
  45:  
  46:             Assert.That(complex01 != complex02, Is.True);
  47:             Assert.That(complex01 != complex03, Is.False);
  48:         }
  49:     }
  50: }

Soy fanático de NUnit, así que si más de alguien no entiende sería bueno que buscara en internet algo de info para ponerse al día (obviamente para correr la unidad de prueba ejemplo necesitas tener NUnit instalado verdad?).

Qué pasó con valores nulos?

Alguien precavido podrá notar que hay una condición que no estamos tomando en cuenta en nuestras pruebas, qué pasaría si uno de los valores de igualdad fuera nulo? Veamos entonces, agreguemos una prueba en la cual “esperaríamos” que si uno de los miebros es Null simplemente retorne false:

   1: [Test]
   2: public void TestEqualNullIsFalse()
   3: {
   4:     ComplexNumber complex01 = new ComplexNumber(1, 0);
   5:     ComplexNumber complex02 = null;
   6:  
   7:     Assert.That(complex01.Equals(complex02), Is.False);
   8: }

Puff, no lo esperabamos! nuestra prueba falla!!!, analicemos un poco nuestro código. Inmediatamente alguien precavido dirá: “podemos agregar un simple if(other == null) return false; y resolveremos totalmente el asunto, pero lo que no contamos es que ya hemos hecho un overload del operador !=, esto nos pone en serios aprietos, ¿cómo podríamos hacer para probar la igualdad a nulo sin usar nuestro operador ya hecho?, claro! usemos el casting al padre de todos los objetos, Object

   1: #region IEquatable<ComplexNumber> Members
   2:  
   3: public bool Equals(ComplexNumber other)
   4: {
   5:     if((object) other != null)
   6:         return (_imaginaryPart.Equals(other.ImaginaryPart) && _realPart.Equals(other.RealPart));
   7:     return false;
   8: }
   9:  
  10: #endregion

Y si al comparar el extremo izquierdo en nuestro operador sobrecargado de igualdad se pasa un elemento nulo? bien arreglemos eso también:

   1: public static bool operator ==(ComplexNumber complex1, ComplexNumber complex2)
   2: {
   3:    if ((object) complex1 == null) return ((object) complex2 == null);
   4:     return complex1.Equals(complex2);
   5: }

Agreguemos una prueba mas a nuestra fixture

   1: [Test]
   2: public void TestEqualOperatorNullWorks()
   3: {
   4:     ComplexNumber complex01 = null;
   5:     ComplexNumber complex02 = new ComplexNumber(1, 1);
   6:  
   7:     Assert.That(complex01 == complex02, Is.False);
   8:     Assert.That(complex02 == complex01, Is.False);
   9: }

Operadores binarios simples

Bien, como lo que nos interesa es representar un número complejo no sería un número si no podríamos operar en el cual número que debería ser, bien, más de alguien dirá: “implementemos un método llamado Add que sirva para añadir, Substract para substraer y así sucesivamente”. Bien, hagamos eso, veamos que resulta.

   1: public ComplexNumber AddComplex(ComplexNumber number)
   2: {
   3:     if(number == null)
   4:         throw new ArgumentNullException("number", "Parameter should not being null");
   5:         
   6:     return new ComplexNumber(_realPart + number.RealPart, _imaginaryPart + number.ImaginaryPart);
   7: }

Por supuesto que debemos implementar el respectivo test de nuestro método

   1: [Test]
   2: public void TestAddComplexNumber()
   3: {
   4:     ComplexNumber complex01 = new ComplexNumber(1, 1);
   5:     ComplexNumber complex02 = new ComplexNumber(1, 2);
   6:  
   7:     ComplexNumber complex03 = complex01.AddComplex(complex02);
   8:     
   9:     Assert.That(complex03.RealPart, Is.EqualTo(2));
  10:     Assert.That(complex03.ImaginaryPart, Is.EqualTo(3));
  11: }

Fácil no?, de igual manera podemos hacer overload de otros operadores matemáticos como -, *, /, % (algo que por tiempo y espacio no haremos, pero en el código de muestra ya incluyo). Para asegurarnos que todo esta bien intentemos un último caso, en el que los miembros sean nulos.

   1: [Test]
   2: [ExpectedException(typeof(ArgumentNullException))]
   3: public void TestAddComplexNumberNull()
   4: {
   5:     ComplexNumber complex01 = new ComplexNumber(1, 1);
   6:     ComplexNumber complex02 = null;
   7:  
   8:     ComplexNumber complex03 = complex01.AddComplex(complex02);
   9:  
  10: }
  11:  
  12: [Test]
  13: [ExpectedException(typeof(ArgumentNullException))]
  14: public void TestAddComplexNumberOperatorNull()
  15: {
  16:     ComplexNumber complex01 = null;
  17:     ComplexNumber complex02 = new ComplexNumber(1, 2);
  18:  
  19:     ComplexNumber complex03 = complex01 + complex02;
  20: }

Bien, hasta aquí llegamos hoy, para la próxima entrega tocaremos el uso de operadores unarios y un par de cositas más, mientras tanto pueden bajar la solución completa con los ejemplos en este link. Como siempre cualquier pregunta o sugerencia estamos a un comment o post de distancia. Saludos!

Parte I

Una respuesta to “Clases y estructuras bien formadas, Parte II”

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

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: