Ir al contenido principal

Herencia y redefinición de métodos


Pepito, el arquitecto de software que vive en mi cabeza, ha irrumpido en mi pensamiento vociferando - ¡ Esto es insostenible !

Al parecer ha clonado nuestro bonito proyecto y ha encontrado flagrantes fallos de diseño. He tratado de explicarle que tenía muchas ganas de sacar adelante las primeras versiones, que las cosas no salen perfectas a la primera, le he reconocido que hay margen de mejora, y que tengo varias cosas apuntadas en mi libreta para retocar. Él seguía enfurecido. - ¡ No puedes enseñar esto al público, qué van a pensar !

En la eterna pugna entre desarrollador y arquitecto, siempre el arquitecto tiene la razón (¿o no? jjj)

Me encantaría que fueran Uds. mismos queridos lectores quienes me propusieran mejoras y alternativas para la plataforma de test, lamentablemente este blog es de muy reciente creación, y no creo que ello vaya a ocurrir, así que de momento nos arreglaremos Pepito y yo solos. No obstante les animo una y otra vez a participar activamente.

Recuerden que disponen del proyecto completo en GitHub
Enigma Software - ZM LABS

Uso correcto de la herencia y la suplantación de métodos.

Cada uno de los Test que definamos van a constituir una clase, con su lógica particular. Pero hemos definido una clase base para cada uno de los test, que contará con el método Start() y las propiedades Estado y Mensajes.

En el objeto test_object.ExecutionProperties hemos definido una propiedad genérica de tipo Object para asociar la clase asociada al test seleccionado. Sin embargo, estábamos asociando la clase derivada, en este caso test1_multithreading_vs_singlethreading, lo que nos obligaba a usar la cláusula CASE para saber a que tipo de dato corresponde la propiedad.

En lugar de esto, la mejor opción en asociar a la propiedad genérica la clase base a la que hemos llamado test_exec

La asignación de la propiedad genérica queda igual que estaba:

_treeelem.TestObject.execution.OBJ = test_types.GetObject(_test_functions, _type);

..pero modificamos el método GetObject para instanciar un objeto del tipo base test_exec y su correspondiente clase derivada.

        public static Object GetObject(test_functions _functions, ZmLabsObjects.enumTestTypes _type)
        {
            test_exec res = new test_exec(_functions);

            switch (_type)
            {
                case ZmLabsObjects.enumTestTypes.test1_multithreading_vs_singlethreading:

                    res = new test1_multithreading_vs_singlethreading(_functions);
                    break;

            }

            return res;
        }

Poca diferencia, pero trascendente, ya que a la hora de ejecutar el test ya no necesitamos hacer un SWITCH según el tipo de objeto instanciado como antes, sino que invocamos el método virtual Start() sea cual sea la clase derivada:

        public static void HiloNegocio()
        {
            var negobject = (test_exec)_testobject.execution.OBJ;
            negobject.Start();

            //switch (_testobject.execution.TestType)
            //{
            //    case enumTestTypes.test1_multithreading_vs_singlethreading:

            //        var obj2 = (test1_multithreading_vs_singlethreading)_testobject.execution.OBJ;
            //        obj2.Start();

            //        break;
            //}
        }

Del mismo modo, en la lectura de las propiedades Estado y Mensajes ya no es necesario dicho SWITCH que evaluaba el tipo de test que se estaba ejecutando, sino que leemos las propiedades de la clase base, heredadas por la clase del test específico:

            test_exec execObject = (test_exec)_testobject.execution.OBJ;

            _estadoProceso = execObject.Estado;

            lstMensajes = execObject.Mensajes.Where(msg => msg.leido == false).ToList();

            foreach (var msg in lstMensajes)
            {
                execObject.SetMsgLeido(msg.id);
            }

            //_testobject.execution.OBJ = execObject;

            //switch (_testobject.execution.TestType)
            //{
            //    case enumTestTypes.test1_multithreading_vs_singlethreading:

            //        var obj2 = (test1_multithreading_vs_singlethreading)_testobject.execution.OBJ;

            //        _estadoProceso = obj2.Estado;
            //        lstMensajes = obj2.Mensajes.Where(msg => msg.leido == false).ToList();

            //        foreach (var msg in lstMensajes)
            //        {
            //            obj2.SetMsgLeido(msg.id);
            //        }

            //        _testobject.execution.OBJ = (test1_multithreading_vs_singlethreading)obj2;

            //        break;
            //}

Una de las ventajas de la herencia es la posibilidad de reutilizar métodos genéricos en todas las clases derivadas, y aquí tenemos un ejemplo magnífico. Se trata del método SetMsgLeido. En este momento el método en cuestión se encuentra implementado en la clase derivada test1_multithreading_vs_singlethreading. Ello nos obligaría a repetir esta funcionalidad en cada uno de los test. Ahora por orden de Pepito voy a trasladar el método a la clase base test_exec, de modo que se convertirá en un método reutilizable en todas las clases derivadas test1_, test2_, etc.

Les mostraré pues como han quedado las clases base y derivada:

    public class test_exec
    {
        public test_types.enumEstadoProceso Estado;

        public List<test_types.mensajes> Mensajes = new List<test_types.mensajes>();
        public test_functions _testobject;

        public test_exec(test_functions p_testobject)
        {
            _testobject = p_testobject;
        }

        public virtual void Start() {  }

        public void SetMsgLeido(Guid id)
        {
            Mensajes.Where(idx => idx.id == id).First().leido = true;
        }

        private void SetMsg(string Msg)
        {
            this.Mensajes.Add(new test_types.mensajes()
            {
                id = Guid.NewGuid(),
                mensaje = Msg,
                leido = false
            });
        }
    }

    public class test1_multithreading_vs_singlethreading : objects.test_exec
    {
        public static List<objects.process_control> _lst_process_control = new List<objects.process_control>();

        public test1_multithreading_vs_singlethreading(test_functions p_testobject) : base(p_testobject) { }

        public override void Start()
        {
            this.Estado = test_types.enumEstadoProceso.Ejecutando;

   //-->> cosas

            this.Estado = test_types.enumEstadoProceso.Finalizado;

        }

        #region Cases

        public TestCases MultithreadingCase(TestCases _test)
        {
   //-->> cosas
        }

        private static void CalcFibo1(int index)
        {
   //-->> cosas
        }

        public TestCases SinglethreadingCase(TestCases _test)
        {
   //-->> cosas
        }

        public TestCases HybridCase(TestCases _test)
        {
   //-->> cosas
        }

        private static void CalcFibo2(int index)
        {
   //-->> cosas
        }

        #endregion
    }

Recuerden amigos dejar cuantos comentarios les parezcan oportunos.




Comentarios

Entradas populares de este blog

Unit Testing

Como programador, he aprehendido la importancia de los test unitarios por la vía del dolor. Por la vía de los mantenimientos de software que se van volviendo más y más inmantenibles día tras día, por la vía de corregir un error con urgencia, subirlo a un entorno de producción habiendo hecho las pruebas funcionales justas, y totalmente orientadas a probar la parte que se ha modificado, para darme cuenta -también dolorosamente- que la corrección del error produce otro error en algún otro punto de la solución, que obliga a revertir el cambio, corregir de nuevo con la presión del negocio en aumento, y subir la corrección de la corrección con los dedos cruzados y las rodillas temblorosas, a sabiendas de que ningún mecanismo me garantiza que no vuelva a pasar lo mismo. Sencillamente no se pueden repetir manualmente todas las pruebas, todas las casuísticas que pudieran haberse visto afectadas por el cambio. Cuesta un poco entender el test unitario como una inversión, pero cuando por fin ...

Test 1: Multithreading vs Singlethreading

¡¡ Bien !! Después de unos cuantos arreglos, ya tenemos disponible el primer TEST para nuestra plataforma. Puesto que me gustaría mediante este blog interactuar con las entradas de Stackoverflow , para este primer test he seleccionado este interesante tema:  multithreading slower than singlethreading Y para introducirnos en la filosofía de los TEST, no es mi intención impartir una conferencia sobre este asunto, sino simplemente implementar las casuísticas que nos interesa evaluar, y medir los rendimientos. Vamos a ello. En primer lugar grabamos en BBDD (a través del recién desarrollado interfaz) el test, y los testcases . La cosa quedaría así: A nivel programático, debemos definir una clase cuyo nombre se corresponda con el campo "Clase" del Test. Y tantos métodos como "Casos" asociemos al test, haciendo igualmente coincidir los nombres. En este punto les recuerdo que pueden descargar el proyecto íntegro en  EnigmaLABS - GitHub Para que la comparació...

Trazabilidad y control de errores - 2ª parte: trazabilidad estructurada

En esta nueva entrega de la publicación  Trazabilidad y control de errores vamos a centrarnos en el que es quizá el más interesante aspecto de la trazabilidad.  Structured Logging es la técnica que nos permitirá realizar análisis automatizados de nuestra trazabilidad, mediante software de detección de eventos. En la primera parte de la publicación incorporamos a nuestra plataforma de test Open Source la librería NLog , y configuramos la generación de dos ficheros de texto plano para trazas, uno para dejar la información de los posibles errores no controlados, y otro para los avisos, o warnings . Además asociamos la consola para crear trazas de información para la depuración. Hoy vamos a configurar la creación de un tercer fichero, que almacenará igualmente información de los errores, pero en este caso guardará la información no en texto plano, sino estructurada, con notación JSON que después podría ser procesada. Para este fin NLOG nos proporciona el JSON Layout . Vamos a ...