Translate

martes, 26 de junio de 2012

MVC explicado aun más

Gráfciamente MVC puede entender asi:





Como sería la implementación concreta en Delphi de este principio ?


Muy simple:


TVista.BotonOnClick(Sender: TObject);
begin
  TModelo.Proceso;
end;


TModelo.Proceso;
begin
  .
  .
end;


Notemos que en este ejemplo no hay una clase Controladora .. porque ? .. porque en realidad el Modelo relacional se comunica con las Vista a través las fuentes de datos (clase TDataSource) 


TModelo.Proceso;
begin
  with TablaCliente do
  begin
    DisableControls;
    try
      First;
      while not Eof do
      begin
        .
        .
        Next;
      end;
  finally
    EnableControls;
  end;
end;


Todo programador Delphi conoce esto. El método DisableControls de un descendiente de la clase TDataSet .. en relacion a MVC, desconecta el Modelo de la Vista .. a la manera Delphi.


Inversamente EnableControls, reconecta la Vista con el Modelo, actualizándola segun el estado del TDataSet a ese momento.


De nuevo .. 'donde estan los controladores ?' .. Los controladores existen dentro de los controles de tipo 'data-aware' y 'saben' como refrescar sus vistas.


O sea, para dar un ejemplo .. el componente TDbEdit,  tiene una parte de Vista (la que se ve y se dibuja en el Form que lo contiene) y otra parte de Controlador.



lunes, 25 de junio de 2012

Es Delphi MVC ? ... Parte III

De como Delphi naturalmente permite implementar MVC .. en buena medida ..


Ya vimos que la pregunta  correcta no es si Delphi es o no MVC .. sino si Delphi permite implementar el principio MVC (para algunos quizás solamente 'patrón') 


Implementaciones de MVC, hay muchísimas .. pero todas usan el patrón de diseño llamado  'El Observador' .. 


Este es un patrón muy conocido y usado. Básicamente consiste en que 'algo' al cambiar, le comunica que cambió a un conjunto de 'observadores' que actuarán en consecuencia, haciendo 'algo', eventualmente nada.


Ya lo dice Wikipedia: 


Es un patrón de diseño que define una dependencia del tipo uno-a-muchos entre objetos, de manera que cuando uno de los objetos cambia su estado, notifica este cambio a todos los dependientes.


Y cual sería la 'cosa' que cambia y cuyos cambios deben ser 'observados' ? .. la respuesta es obvia: El Modelo.


Ahora llegamos a un punto crucial .. como permite representar Delphi a un 'Modelo' ?? .. La respuesta es:
  • Delphi permite representar un Modelo como mejor a uno la parezca, porque es un compilador potentisimo y muy sofisticado
  • La manera obvia de representar un Modelo es con Clases y propiedades .. esto es bastante claro. Acá habrá que resolver la 'persistencia' del Modelo.
  • Delphi, sin embargo estar orientado naturalmente a un Modelo 'relacional' .. o sea basado en Tablas y columnas.
Es tan fuerte la implementacion del 'modelo relacional' en Delphi .. tan madura, sofisticada y versátil .. que justamente da lugar a la experiencia RAD que en mi opinión es algo único, distintivo de Delphi.

O sea la posiblidad de diseñar un modelo de tipo relacional y .. precisamente conectarlo a las Vistas de manera inmediata.

A esta altura ya todos sabemos de que estoy hablando .. el patrón del 'Observador' esta naturalmente implementado en Delphi a traves de la conexion entre descendientes del componente TDataSet y el componente TDataSource .. el que a su vez conecta naturalmente con las Vistas .. las que responden automáticamente a cualquier cambio que se produce en el Modelo (relacional)


Que es lo que ocurre cuando un programador que no sabe de MVC, empieza a escribir código en Delphi  ??


Pues que 'mezcla' .. todo en una sola unidad descendiente de  TForm .. usualmente. Entonces el Modelo la Vista y los Controladores .. colusionan en un mismo espacio entremezclados y vuelven dificil el mantenimiento y la extensión.


Es esto culpa de Delphi ?? .. pues si y no. 
  • Si .. porque Delphi permite escribir tan pero tan facil todo junto que pocos se toman el trabajo de separar el código del Modelo del código de las Vistas .. 
  • No .. porque, Delphi propone desde hace mucho tiempo escribir el código de 'datos' (el Modelo relacional) en la clase TDataModule, diseñada específicamente para colocar el código relacional (Componentes, Métodos, Interfases .. etc)
Y los controladores ? .. los controladores, es la parte donde Delphi realmente no propone en forma nativa una implementación formal ... o sea una clase 'TController' .. o algo por el estilo.


Y entonces como se controla una aplicacion ? .. Esto la sabe cualquier programador de Delphi !!! .. escribiendo código en los Eventos de los 'Controles' que están sobre las Vistas ..


De hecho, ese código de control de la aplicacion hace las dos cosas esperadas de un controlador:
  • Ejecuta métodos del Modelo (Ejemplo: El Operador presiona un botón de 'Grabar' y el controlador ejecuta el método .. Modelo.GrabarDatos())
  • Y eventualmente acciona sobre la Vista misma (Ejemplo: El Operador presiona un ToolButton y deshabilita otros o cambia en algo la apariencia de la Vista, ocultando o mostrando controles)
Finalmente no deja de ser importante notar que los controles del tipo 'Data aware' .. o sea conectados a una Fuente (a través en realidad del objeto interno TDataSetLink) ... tienen su propio Controlador interno.

viernes, 22 de junio de 2012

Es Delphi MVC ? .. Parte II

Mas sobre MVC 


En la primer parte, hemos visto que MVC es un 'patron' de diseño. Realmente es una gran abstracción que de manera muy general enmarca el diseño de cualquier aplicación, sea esta del tipo que sea.


Por lo tanto MVC es una suerte de principio general de diseño. Independientemente de la aplicación es posible verificar que:
  • Siempre existe la 'cosa' sobre la que se construye una o mas aplicaciones .. sea esta una Empresa, Red Social, Persona, Institucion, Objeto ... A la abstracción de esta 'cosa' la llamamos Modelo.
  • Existen infinitas formas de 'ver' el Modelo .. pantallas, reportes, graficos. A estas formas se les da el nombre genérico de Vistas.
Es completamente claro que los cambios en la estructura del Modelo, usualmente impactarán sobre las Vistas.

Por ejemplo si agregamos un nuevo dato al Modelo, algunas Vistas pre-existentes deberán ser modificadas para reflejar este cambio. Lo mismo si son modificados o elminados datos del Modelo.

Notablemente los cambios en la funcionalidad del Modelo .. en principio no afectará a las Vistas. Por ejemplo, es posible cambiar una regla de cálculo sin que haya que tocar una sola Pantalla. Lo mismo ocurre con las validaciones .. lo mismo con procesos internos que  no tienen reflejo alguno en las Vistas.

En cuanto a las Vistas, la situacion es mas simple. Esta claro que en principio ningun cambio en ellas debiera impactar el Modelo en ningun sentido.De hecho .. una Vista puede desaparecer sin que al Modelo le impacte en nada.

Por lo tanto Modelo y Vistas se hallan en una relación estructural del tipo uno a muchos, con un tipo de acople unidireccional y de baja intensidad. 

Sintetizando ..
  • Hay un Modelo y cero, una o mas vistas que dependen de este.
  • Los cambios en la estructura del Modelo, afectan en general a algunas Vistas. 
  • Los cambios funcionales en el Modelo, en general no afectan a ninguna Vistas.
  • Los cambios en las Vistas no afectan al Modelo.
Lo notable es que una aplicación, surge de la unión entre el Modelo y las Vistas a través de los Controladores, permitiendo la interacción del Operador y otros tipos de interacciones mas o menos automáticas.


En ese sentido los controladores serian el codigo 'específico' que no es del Modelo y que hacen funcionar a cada Aplicacion. Y esto tambien es una importante abstracción.


Suficiente de MVC !! .. vamos ahora a ver como Delphi en mi opinión permite programar según este principio general. 


Sigue en la Parte III 





Es Delphi MVC ? .. Parte I



Es Delphi MVC ? .. o no lo es ? .. Pero ante todo, que es MVC ?

MVC es un patron de diseño, basado en una abstraccion que describe las tres partes que (segun esta abstraccion) constituyen un Sistema y la forma en que interactuan entre si.

Estas partes son el Modelo, las Vistas y los Controladores. Y es facil de entender .. una vez que se comprende que MVC es una abstraccion y no una implementacion concreta.

Que es el Modelo ? .. es la representacion abstracta de 'algo' (tipicamente una Empresa) con sus objetos, reglas, conceptos, etc .. (Clientes, Proveedores, Comprobantes, Politicas de Precios, etc) .. pero no necesariamente una Empresa, puede ser cualquier otra cosa 'viva' .. 

Que son las Vistas ? .. simplemente las distintas representaciones mas o menos complejas de aspectos de un Modelo. Pueden ser Consultas, Graficos, Formularios, Reportes, etc .. 

Que son los Controladores ? .. son mecanismos construidos especificamente para que las Vistas y el Modelo interactuen con o sin la presencia de un Operador. A estos controladores los llamamos 'codigo' .. 'logica de control' .. 'manejadores de eventos' .. 

Es importante entender a continuacion lo siguiente:

El Modelo puede existir, como representacion abstracta de 'algo' sin que haya una vista y mucho menos controladores. O sea .. el modelo de una Empresa puede existir sin que jamas se construya una (o mas de una) aplicación con sus Pantallas, Reportes, etc.

Las Vistas se construyen y agregan segun deseo o conveniencia y el que una Vista cambie, no cambia en nada al Modelo. Por ejemplo en vez de mostrar datos en formato de registro pasa a mostrarlos en formato tabular .. o se muestran mas datos .. o simplemente cambia el color de fondo del Formulario .. cambia la Vista, pero no el Modelo.

Los controladores permiten la interaccion concreta entres las Vistas y el Modelo .. en un ida y vuelta .. Si 'algo' ocurre en una Vista que requiere de codigo de control - por ejemplo el Operador presiona el boton 'Okey' - .. se ejecutara ese codigo de control que accionara sobre el Modelo (por ejemplo mandando los datos de la Vista para que sean grabados y de esa forma alterando el Modelo) .. y al reves .. si 'algo' ocurre en el Modelo, a traves de un mecanismo de transmision este le informara a cada uno de los Controladores los que a su vez accionaran sobres las Vistas para que ajusten su representacion del Modelo (por ejemplo cambian datos en el Modelo que deben ser resfrescados en las distintas Vistas que representan esos datos)

Dijimos al principio .. 'MVC es un patron de diseño, basado en una abstraccion ..' .. hasta aqui describimos la abstraccion sobre la que esta basado el patron MVC .. pero, y cual es ese patron ?

Simplemente que el Modelo y las Vistas deben existir separadamente - lo mas separadamente posible - de forma tal que un cambio en el diseño del Modelo impacte lo minimo posible en las Vistas y al reves .. un cambio en cualquiera de las Vistas no cambie en nada al Modelo.

Ademas que el diseño general permita la interaccion entre el Modelo y las Vistas .. a traves de los Controladores. Como se dijo antes .. los cambios en el Modelo llegan a las Vista a traves de los Controladores y los cambios en las Vistas llegan al Modelo tambien a traves de estos.



Que tiene que ver Delphi con todo esto ? .. pues nada .. Delphi es un marco de desarrollo de aplicaciones que permite o no implementar el patron MVC con mayor o menor facilidad que otros entornos de programacion.

En la segunda parte de este artículo veremos como en realidad .. Dephi permite implementar en gran parte este patron de manera casi natural.

Otra forma de escribir SQL



Es muy comun ver Sql embebido en codigo de la siguiente forma:

sql:=
  'select cmp.fecha,cmp.cliente from comprobantes cmp left join movimientos mov '+
  'on nmov.comprobante=cmp.comprobante left join clientes cli on cli.cliente=cmp.cliente '+
  'left join articulos art on art.articulo=mov.articulo left join comprobantesimpuestos cim on '+
  'cim.comprobante=cmp.comprobante '+
  'where cmp.fecha between '+QuotedStr(datetostr(fechadesde))+' and '+QuotedStr(datetostr(fechahasta))+' and '+
  'cli.cliente='+inttostr(cliente)

Los dos problemas que presenta esta forma de embeber sql en codigo, aparte de lo puramente estetico, son:

Es dificil de leer el sql .. o sea entender que hace.
La asignacion de valores de parametros exige la conversion en cada caso y es facil equivocarse. Por ejemplo las fechas deben ser convertidas y colocadas entre comillas.

Es posible escribir SQL embebido de una manera mejor ?

Si .. pero como ?

Una vez un gran programador me djo .. 'no te preocupes por el como sino por el que'

Por lo tanto empezemos por el 'que'. Imaginemos una clase que se dedique a construir un texto SQL ..

with TTextoDeSql.Create([
  'SELECT',
    'CMP.FECHA,',
    'CMP.CLIENTE',
  'FROM',
    'COMPROBANTES CMP',
  'LEFT JOIN',
    'MOVIMIENTOS MOV ON MOV.COMPROBANTE=CMP.COMPROBANTE',
  'LEFT JOIN',
    'CLIENTES CLI ON CLI.CLIENTE=CMP.CLIENTE',
  'LEFT JOIN',
    'ARTICULOS ART ON ART.ARTICULO=MOV.ARTICULO',
  'LEFT JOIN',
    'COMPROBANTESIMPUESTOS CIM ON CIM.COMPROBANTE=CMP.COMPROBANTE',
  'WHERE',
    'CMP.FECHA BETWEEN @DESDE AND @HASTA AND',
    'CMP.CLIENTE=@CLIENTE ']) do
try
  Parametro('DESDE',dFechaDesde);
  Parametro('HASTA',dFechaHasta);
  Parametro('CLIENTE',nCliente);
  Result:=Texto;
finally
  Free;
end;

Es mas legible no ? ...  No solo eso, la clase TTextoDeSql, se ocupara de transforma los valores asignados a los distintos parametros en literales validos para una sentencia SQL, sin que hay que preocuparse por comillas, conversiones, concatencion y demas.

Clases colaborativas en Delphi. Parte II


En el artículo anterior revisabamos las formas de agregar nueva funcionalidad a una Clase, intentando mantener el codigo lo mas ordenado posible ..

En la practica esto es mas dificil de lo que parece y cualquier programador sabe que al cabo de algunas extensiones el codigo original, se transforma en una secuencia de codigo, dificil de leer y fundamentalmente dificil de extender o modificar.

Las tres formas explicadas anteriormente son:

La programacion lineal

Es simplemente escribir en un solo metodo todo la funcionalidad, lo que resulta naturalmente en un codigo absolutamente no reusable.

Pero la programacion lineal tiene en general una ventaja indudable .. es muy facil de leer !! .. porque el flujo procede de la primer linea a la ultima una despues de otra.

Este modo a pesar de ser tabu .. es de hecho utilizado por miles de programadores que simplemente no quieren tomarse la molestia de estructurar codigo.

La programacion estructurada

Simplemente, consiste en dividir un problema en partes e implementar cada parte en un nuevo metodo. De esta forma es posible identificar mejor las parte de un problema y eventualmente reusar aquellas que deban ser utlizadas en distintos contextos.

La programacion estructurada con sub rutinas

Similar a la anterior, pero las partes de un problema quedan estructuradas como sub rutinas de un metodo. En ese sentido, el metodo incluye todos los recursos necesarios para su implementacion: subrutinas, sub subrutinas (etc),
variables locales, sobrecarga .. y hasta recursividad ! (ver Clases Colaborativas Parte I)

Las clases colaborativas

Este enfoque no tiene nada de 'nuevo' .. tan solo que es muy poco usado. Consiste en la creacion de clases que se dedican a resolver problemas especificos. A la clase asi dedicada, no le 'importa' nada mas que el problema que tiene que resolver.

Todo el espacio de esta clase, esta precisamente dedicado a resolver el problema, incluyendo sus metodos y variables de instancia.

Si dos problemas se parecen, las soluciones pueden derivarse de clases colaborativas basicas o abstractas, que sientan las base de resolucion de un problema, en general.

La implementacion de estas clases, es sorprendetemente simple y promueve la resolucion ordenada de problemas o aspectos de una clase superior.

Por ejemplo:

TClase = class
public
   procedure ProblemaUno;  
end;

TClaseColaborativa = class;
TTipoDeClaseColaborativa = class of TClaseColaborativa;

TClaseColaborativa = class
private
    oClase: TClase;
protected
   procedure Inicio; virtual;
   procedure Proceso; virtual;
   procedure Fin; virtual;
   class function TipoDeClase: TTipoDeClaseColaborativa; virtual; abstract;    
public
   constructor Create(const oClasePar: TClase);
   procedure Resuelve; overload;
   class procedure Resuelve(const oClasePar: TClase);  overload;
end;

TResuelveProblemaUno = class(TClaseColaborativa )
protected
    class function TipoDeClase: TTipoDeClaseColaborativa;  override; 
end;

TResuelveProblemaDos = class(TClaseColaborativa)
protected
    class function TipoDeClase: TTipoDeClaseColaborativa; override;  
end;

implementation

{ TClase }

TClase.ProblemaUno;
begin
   TResuelveProblemaUno.Resuelve(Self);
end;

{ TClaseColaborativa }

TClaseColaborativa.Create(const oClasePar: TClase)
begin
   inherited Create;
  oClase:=oClasePar;
end;

TClaseColaborativa.Resuelve(const oClasePar: TClase);
begin
   with TipoDeClase.Create(oClasePar) do
  try
     Resuelve;
  finally
     Free;
  end;
end;

TClaseColaborativa.Resuelve;
begin
   Inicio;
   try
      Proceso;
   finally
      Fin;
   end;
end;

TClaseColaborativa.Inicio;
begin
end;

TClaseColaborativa.Proceso;
begin
end;

TClaseColaborativa.Fin;
begin
end;

{ TResuelveProblemaUno }

TResuelveProblemaUno.TipoDeClase; override;
begin
   Result:=TResuelveProblemaUno;
end;

{ TResuelveProblemaDos }

TResuelveProblemaDos.TipoDeClase; override;
begin
   Result:=TResuelveProblemaDos;
end;

Lo importante aqui son dos cosas ...

   TClase.ProblemaUno;
   begin
      TResuelveProblemaUno.Resuelve(Self);
   end;

La implementacion del ProblemaUno .. queda a cargo de la clase dedicada que es TResuelveProblemaUno, a traves del metodo de clase Resuelve. Y ademas .. 

   TClaseColaborativa = class
   .
   .
   end;

Esta es un clase basica que presta el marco general para las clases resolvedoras de problemas especificos que seran heredadas de esta misma.

Clases colaborativas en Delphi. Parte I


Es posible extender un módulo sin generar una ensalada de código, nombres de procedimientos y variables ?

Si es posible .. pero como ?

Supongamos que un modulo complejo requiere de constantes ampliaciones .. normalmente estas ampliaciones se agregan como metodos del modulo, generando un espacio propio para la descripcion y resolucion de cada nueva funcionalidad agregada.

Por ejemplo, si el modulo es un TDataModule, con varias fuentes de datos, tipicamente requerira para el procesamiento extendido de agregar nuevos metodos y variables de instancia. 

Si el modulo de datos implementa el modelo de una Orden Compra, esta podria ser cargada a partir de un Presupuesto. Por lo tanto habria que agregar cierta cantidad de funcionalidad relativa a la carga, como ser

        Cargar el  Presupuesto
        Cargar el Detalle de la Orden de Compra a partir del Presupuesto
        Actualizar Totales de la Orden de Compra
        Finalizar el proceso

Por supuesto es posible escribir un solo metodo que implemente toda esta funcionalidad. El precio ... una posible ensalada de código, dificil de extender (pero no necesariamente de leer)

Pero normalmente se escribe un metodo de cabecera y sub metodos que implementaran partes especificas de la funcionalidad requerida.

      TModuloDeDatos.CargarDePresupuesto(const nPresupuestoPar: Integer);
      begin
         try
              IniciaCargaDePresupuesto(nPresupuestoPar);
              CargaPresupuesto;
              CargaDetalleDePresupuesto;
              RecalculaTotales;
          finally
              FinalizaCargaDePresupuesto;
          end;
     end;

Este es el enfoque estructurado clasico, la resolucion de un problema se divide en partes y cada una de estas partes se ataca relativamente por separado.

Sin embargo al implementarse esta solucion dentro de una clase pre-existente, agranda esa clase introduciendo funcionalidad especifica, la que luego en la practica no es tan facil de modificar o extender sin impactar en la clase anfitriona.

A medida que el modelo de datos se extiende introduciendo mas y mas funcionalidad - por ejemplo cargar la Orden de Compra de una Solicitud en vez de un Presupuesto - el modelo como conjunto empieza a volverse cada vez mas confuso y dificil de mantener. 

En parte por la dificulta en ir inventando nombres nuevos, y en parte porque la reutilizacion de codigo comun a las distintas funcionalidades empieza a introducir la tendencia a duplicar codigo.

Existe una forma muy poco conocida de estructurar codigo sin introducir nuevos metodos .. o muy poco usada. Es la de las subrutinas. Esta es una funcionalidad basica de Pascal, verdaderamente notable.

Dada un metodo ..

    TClase.Metodo
    begin
    end;

Es posible escribir de manera inmediata lo siguiente:

TClase.Metodo;

   procedure Inicio;
   begin
   end;

   procedure Proceso;
   begin
   end;

   procedure Fin;
   begin
   end;

begin
   Inicio;
   try
      Proceso
   finally
      Fin;
   end;
end;

Mas aun, una subrutina puede anidar sub subrutinas y asi siguiendo ..

TClase.Metodo;

   procedure Proceso; 

     function Detecta: Boolean;
     begin
     end;

    procedure SeteaValores;
    begin
    end;

   begin
     with Tabla do
     begin
        while not Eof do
        begin
           if Detecta then
             SeteaValores;
          Next;
        end;
     end;   
   end;

begin
end;

Incluso mas !!! .. una subrutina puede ser sobrecargada ! .. O sea:

TClase.Metodo;

  procedure Proceso; overload;
  begin
  end;

  procedure Proceso(const nParametro: Integer); overload;
  begin
  end;

begin

end;

Las subrutinas llevan sus propias variables locales en el stack .. y no se confunden con las variables del mismo nombre de un nivel de anidamiento superior ..

TClase.Metodo(const nI: Integer);
var
  nJ: Integer;

   procedure Proceso(const nI,nJ: Integer);
   begin
     // El Parametro nI de Proceso .. no es el Parametro nI de TClase.Metodo
     // El Parametro nJ de Proceso ... no es la variable local nJ de TClase.Metodo
   end;

begin
end;

De esta forma es posible describir encapsulada y estructuradamente el espacio de un problema sin salir nunca de alcanze de un Metodo de Clase, agregando tantas variables locales y sub rutinas anidades como haga falta para describir adecuadamente el problema en cuestion. Mas aun .. este enfoque no introduce problemas semanticos, ya que los nombres de metodos y variables son estrictamente locales y no se confunden con nombres identicos de metodos y variables en la clase contenedora.

Sin embargo este enfoque muy rapido y practico tiene como inconveniente que si una subrutina de un metodo repite codigo de otra subrutina en otro metodo, estariamos introduciendo codigo repetido.

La solucion obvia, es ... subir el nivel de esa subrutina al de un metodo de la Clase contenedora.

O sea ..

TClase.MetodoUno;

   procedure Proceso;
   begin
      MetodoTres;
   end;

begin
end;

TClase.MetodoDos;

   procedure Proceso;
   begin
      MetodoTres;
   end;

begin
end;

TClase.MetodoTres;
begin
end;

El TClase.MetodoTres .. es entonces una suerte de recurso compartido hibrido del TClase.MetodoUno y TClase.MetodoDos;

Existe sin embargo un enfoque adicional que permite estructurar, encapsular y especializar el codigo de nueva funcionalidad agregada a una Clase, con un minimo de acople.

Sigue en la Parte II