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

Test 3: ¿Son eficientes los ORMs?

Bienvenidos amigos. Me complace anunciar que por fin estrenamos la categoría " SQL Server Tips ", y lo hacemos por todo lo alto, entrando de lleno en un aspecto altamente polémico entre programadores. ¿Es eficiente un ORM en los accesos a datos? Ya conocen la filosofía de nuestros Tests, no vamos a teorizar demasiado, pero sí una pequeña base va a ser necesaria para conseguir una buena respuesta a nuestra pregunta. He leído un interesante  artículo de nuestros súper amigos de Deloitte ( cuando usar ORM ) argumentando que el uso o no de un ORM hay que decidirlo en relación a la complejidad de nuestro modelo de datos, y al rendimiento que requeriremos en nuestras soluciones, pero, ¿cuándo no deseamos el mejor rendimiento para nuestro software? Lo cierto es que, como ya hemos visto, el ORM facilita mucho las cosas, aporta claridad al código, de eso no cabe duda, pero, ¿es eficiente? He ahí la cuestión. Sobre este asunto vamos a poner a funcionar nuestros apreciados Test. C...

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 ...

Proyecto GEOW. Implementando el patrón CQRS

Me siento muy feliz de poder ofrecerles esta nueva publicación y este nuevo proyecto Open Source. Naturalmente seguiremos evolucionando el proyecto de tests de c#, pero he querido hacer este paréntesis para hablarles del patrón CQRS , o lo que es lo mismo,  Command Query Responsibility Segregation . Una primera versión plenamente operativa del proyecto GEOW está ya disponible para todos Uds. en  GitHub . Les recomiendo encarecidamente que la clonen y jueguen con ella. No solo tiene un elevado potencial pedagógico , además sus propiedades visuales son altamente hipnóticas, por lo que les pido mucha precaución a la hora de ponerlo a funcionar :)  Por esta misma razón me he decidido a crear un canal en YouTube para irles mostrando videitos con los resultados de los dos proyectos que nos traemos entre manos. Espero lo disfruten. Son muchos los detalles técnicos que se desprenden en esta publicación, además de la propia implementación del patrón...