Programacion orientada a objeto avanzada con xHarbour (VI)

Antes de seguir con el tema quiero agradecer a Manu Expósito su colaboración en este proyecto, ya se que ofreció a revisar, optimizar y completar las clases ArrayList, ListIterator, Navigator y Object.

Comentar también que se ha añadido una clase padre Object de la que heredarán todas las clases que usemos en nuestros proyectos.

Descargar nueva versión de la libreria complements.lib.
Descargar documentación de la libreria complements.lib.

6.- Programación por capas:

Como ya se adelantó en el artículo anterior, vamos a aprender a programar usando la tecnología Modelo-Vista-Controlador, que es una programación por capas en la que separamos la vista (o los formularios), el modelo (las clases que contienen los datos) y el controlador (lo que comunicará el modelo con la vista).

Modelo Vista Controlador con Persistencia

Modelo Vista Controlador con Persistencia

En el diagrama podemos ver como funciona la comunicación entre las capas del MVC. Es importante respetar esta comunicación, la vista no puede ni debe comunicar directamente con el modelo en nigún caso, siempre se comunicará a través del controlador.

Si nos fijamos bien en la parte del modelo vemos que este se compone de dos partes. Esto no tiene por que ser necesariamente así, aunque sí muy recomendable para dar claridad al código y poder programar aplicaciones que usen más de un motor de datos distintos fácilmente.

Seguiremos unas sencillas reglas para programar usando MVC+Persistencia:

  • La aplicación arrancará desde un controlador (podemos tener un controlador principal y tantos controladores secundarios como sean necesarios).
  • Un controlador llamará a otro controlador.
  • Desde un controlador llamaremos a los formularios y lo ejecutaremos.
  • Desde un controlador llamaremos al modelo.
  • Tentremos tantas clases en el modelo como sean necesarias. Las clases del modelo que no pertenezcan a la capa de persistencia no contendrán código alguno que sea de acceso a datos.
  • Tendremos una única capa de persistencia que comunicará las clases del modelo con los datos. La capa de persistencia estará compuesta por tantos archivos como sean necesarios (no tiene que estar todo en un solo prg, es más, se aconseja dividirla en tantos archivos como objetos del modelo existan).

Siguiendo estas instrucciones, veamos el diseño de un mantenimiento simple de conceptos (id, descripción).

Empezaremos con la clase Concepto: (perdonad que no este identada, pero este editor del wordpress es penoso del todo).

CLASS Concepto FROM Object

PROTECTED:

DATA id INIT 0
DATA descripción INIT “”

EXPORTED:

METHOD new() CONSTRUCTOR
METHOD setId( id )
METHOD getId()
METHOD setDescripcion( descripcion )
METHOD getDescripcion()
METHOD grabar()
METHOD borrar()
METHOD toString()
METHOD toArray()

ENDCLASS

METHOD new() CLASS Concepto
RETURN self

METHOD setId( id ) CLASS Concepto
::id := id
RETURN nil

METHOD getId() CLASS Concepto
RETURN ::id

METHOD setDescripcion( descripcion ) CLASS Concepto
::descripcion := descripcion
RETURN nil

METHOD getDescripcion() CLASS Concepto
RETURN ::descripcion

METHOD grabar() CLASS Concepto
PersistenciaConcepto():grabar( self )
RETURN nil

METHOD borrar() CLASS Concepto
PersistenciaConcepto():borrar( self )
RETURN nil

METHOD toArray() CLASS Concepto
RETURN { ::getId(), ::getDescripcion(), self )

METHOD toString() CLASS Concepto
RETURN “”

Ahora definieremos la clase de la persistencia:

CLASS PersistenciaConcepto

PROTECTED:

METHOD siguienteId() // este método solo es accesible desde dentro de la clase

EXPORTED:

METHOD lista()
METHOD cargaDatos( id )
METHOD grabar( objeto )
METHOD borrar( objeto )

ENDCLASS

METHOD lista() CLASS PersistenciaConcepto
// Devuelve una lista de todos los conceptos de la tabla.

local lista := ArrayList():New() //lista con los conceptos
local ds //dataset de Xailer para leer los datos
local concepto //objeto concepto

//Usamos el un objeto de la clase PersistenciaConexion para conectar con el
//motor de datos deseado.
//El código está explicado más adelante.

WITH OBJECT PersistenciaConexion():conectar()
// Creo un dataset para traerme los datos desde la base de datos.

dS := :CreateDataSet( “SELECT * FROM concepto” )

WHILE !dS:EoF()
//creamos un objeto de la clase concepto y lo llenamos con
//los datos del dataset

WITH OBJECT concepto := Concepto():new()
:setId( dS:id )
:setDescripcion( dS:Descripción )
END WITH

//añadimos el objeto concepto a la lista
lista:add( concepto )

//siguiente elemento del dataset
dS:Skip()

END DO

//finalizamos el dataset, ya no nos hace más falta
ds:End()

//cierramos la conexion del datasource, tampoco lo necesito ya
:desconectar()

END WITH

//devolvemos la lista de conceptos
RETURN lista

METHOD cargaDatos( id ) CLASS PersistenciaConcepto
// Devuelve el concepto que tenga ese id o un concepto vacio si no lo encuentra.
local ds
local concepto := Concepto():new()
IF !Empty( id )
WITH OBJECT PersistenciaConexion():conectar()
// Dataset para traerme los datos desde la base de datos.
dS := :CreateDataSet( “SELECT * FROM concepto” + ;
“WHERE id = ” + str( id ) )

IF !Empty( dS:id )
//creamos un objeto de la clase concepto y lo llenamos con
//los datos del dataset
WITH concepto := Concepto():new()
:setId( dS:id )
:setDescripcion( dS:Descripción )
END WITH
END IF

//finalizamos el dataset, ya no nos hace más falta
ds:End()
//cierramos la conexion del datasource, tampoco lo necesito ya
:desconectar()
END WITH
END IF

//devolvemos el objeto concepto
RETURN concepto

METHOD siguienteId() CLASS PersistenciaConcepto
// Devuelve el siguiente valor para id.
// Necesario ya que sqlIte no tene autoincrementales
local dS
local id
WITH OBJECT PersistenciaConexion():Conectar()
dS := :creaDataSet( “SELECT max( id ) AS id FROM concepto” )
IF Empty( dS:Id )
id := 1
ELSE
id := dS:Id + 1
END IF
dS:End()
:desconectar()
END WITH
RETURN id

METHOD grabar( objeto ) CLASS PersistenciaConcepto
//graba en la base de datos los datos de un objeto concepto.
local command
IF Empty( objeto:getId() )
//si el id del objeto concepto esta vacio hacemos INSERT
obj:setId( ::siguienteId() )
command := “INSERT INTO concepto (id, descripcion ) VALUES ” + ;
Str( objeto:getId() ) + “, ” + ;
Chr( 34 ) + objeto:getDescripcion() + Chr( 34 ) + ” )”
ELSE
// si tiene valor, hacemos UPDATE
command := “UPDATE concepto SET ” + ;
“decripcion = ” + Chr( 34 ) + objeto:getDescripcion() + Chr( 34 ) + ” ” + ;
“WHERE id = ” + Str( objeto:getId() )
END IF

IF !Empty( command )
PersistenciaConexion():execute( command )
END IF

RETURN nil

METHOD borrar( objeto ) CLASS PersistenciaConcepto
local command
command := “DELETE concepto WHERE id = ” + Str( objeto:getId() )
IF !Empty( command )
PersistenciaConexion():execute( command )
END IF
RETURN nil

Antes de continuar creo que es importante puntualizar la posibilidad que tenemos en xHarbour de ejecutar un método de determinada clase de forma directa sin necesidad de instanciar un objeto de la misma (Por ejemplo, PersistenciaConexion():execute( command ) ).

También, si te has dado cuenta, haciendo uso de la capa de persistencia, cambiar de un motor a otro es tremendamente sencillo; sólo hay que cambiar el código necesario de la capa de persistencia sin tocar para nada una linea de nuestra aplicación.

Anuncios

10 comentarios

  1. Excelente ejemplo.
    Tomar en cuenta que se adquiere mayor relevancia cuando las capas no estan en el mismo ejecutable y la mayoría de las veces ni siquiera en el mismo equipo.

  2. José, gracias por compartir el material.
    Una consulta de novato: ¿ el método lista() de PersistenciaConcepto siempre ‘levanta’ toda la ‘coleccion’ de objetos; o se puede hacer un select con alguna cláusula where ?

  3. Una cosa que no entiendo en la gráfica: porque hay una flecha que indica que el controlador se comunica con la persistencia? Según entiendo el modelo es la forma de abstraer todo lo que tenga que ver con la persistencia, sino, el controlador ‘sabe demasiado’ respecto de la implementación de BBDD que estoy haciendo. Probablemente tengo un punto de vista diferente ya que uso el MVC con PHP, entonces el paradigma de lenguaje es diferente. Pero creo que el controlador no debe saber nada de persistencia, eso es tarea excluyente del modelo, o me equivoco?

    • Carlos,

      La capa de persistencia pertenece al modelo. El controlador lee los datos de la base de datos a través de la persistencia, creando los objetos necesarios que serán mostrados posteriormente en la vista. Los objetos, como los he diseñado, no tienen métodos que carguen los datos desde la persistencia; es la persistencia la encargada de crear el objeto, llenarlo de datos y devolverlo, y, la única capa que puede hacer esto es la del controlador.

      Gracias por el comentario.

      Un abrazo.

    • Además de lo comentado, si te fijas bien, el controlador no sabe nada de la base de datos, solo ejecuta el método correspondiente, pero nada de instrucciones de la base de datos.

      Saludos

  4. Probablemente sea una cuestion de nomenclatura. Como mi experiencia con MVC viene por el lado de PHP, donde hay específicamente clases que se llaman Controller, Model, View, y la unica comunicación que hay es cuando el controlador le manda mensajes (usa metodos) de la clase Model. Model devuelve los datos, ya sea listas, objetos unitarios, simples o complejos, pero siempre es la clase Model que crea objetos de las clases específicas.
    Generalmente se crea una clase, por ejemplo Concepto_Model, que es la que retornara listas de Conceptos, o conceptos específicos, etc.
    A ver si un dia de estos nos podemos juntar así vemos directamente el código y podemos estudiar la forma a la que me acostumbré a usar el MVC.

    Un abrazo

  5. Carlos,

    Tu clase Concepto_Model es lo mismo que mi clase PersistenciaConcepto.

    Un abrazo.

  6. […] Una vez que hemos revisado y depurado el modelo de datos, pasaremos a la redacción de las clases tal y como se comentaba en los artículos sobre Programacion orientada a objetos avanzada con xHarbour. […]

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: