Model View Controller in Harbour

José Luis Sánchez
Harbour Magazine
Published in
9 min readJun 21, 2018

This article is a translation from ‘Modelo Vista Controlador en Harbour’ written by Manu Expósito. Manu is an spanish database expert who has developed Harbour Data Objects, a class hierarchy to access relational databases from Harbour. You can contact Manu in the email hdoexpoATgmailDOTcom. Any mistake in the traslation is only mine, and I apologize for it.

In this article I’ll try to explain in a practical way how to develop using the MVC design pattern in Harbour.

I would also like it yo be an starting poing, so through your comments and reviews, improve the article itself and the example code.

Let’s start !

Do you know what is a design pattern in programming ?

For many years all the programmers have faced some usual problems and every one solved in a different way. Comparing the solutions in fact there were many points in common. Someone studied this challenges and created a guidelines to solve them wihout using an specific language. Therefore, a design pattern is a defined technique to solve a programming problem. From here I leave you to search more information.

What is the Model View Controller (MVC) design pattern ?

It’s a a way to solve a problem in programming that consists in divide the problem in three layers: the model, the view and the controller. Each layer is specialized in solving its part of the problem. It also can be used with other desing patterns.

This pattern is used mainly in web development, but we can use it also in other kinds of programming.

What is the model ?

You can find a very good explanation of this design patern at Wikipedia — , but I’m going to show it in a practical way.

The model represents the information that our software will manage.

It has to be self-contained, that means that it can’t depend of anything external.

Tiene que ser autosufuciente, quiero decir que no pude depender de nada externo. To obtain the information that define it, it has to access to internal mechanisms that would be use of other patterns, for example use a DAO service to access the information stored in a database. But it also could be retreived fron an XML file, a JSON or a string.

The model doesn’t have to know the existence of the other components, neither the controller nor the view. In fact it should be completely disconnected from them.

As a general rule, classes that represent the model are usually created.

We could create an abstract class and inherit from it the general features to create an specialized class from wich we will instantiate the model objects that we’ll need.

In thos post, I’m going to create a coin converter as a sample.

This is the abstract class that I suggest for the conversion of any coin:

//----------------------------------------------------------------//

#include "HbClass.ch"

//------------------------------------------------------------------
// Class to convert any coin to another

CREATE CLASS TConversorModel

PROTECTED:
DATA cambio
DATA resultado

EXPORTED:
METHOD new( valorCambio ) CONSTRUCTOR
METHOD getResultado()
// SET GET
METHOD setCambio( cambio )
METHOD getCambio()

PROTECTED:
METHOD convMul( cantidad )
METHOD convDiv( cantidad )

END CLASS

//------------------------------------------------------------------
// Constructor

METHOD new( valorCambio ) CLASS TConversorModel

if ValType( valorCambio ) == "N"
::cambio := valorCambio
end if

return Self

//------------------------------------------------------------------
// Conversion from euro to another coin

PROCEDURE convMul( cantidad ) CLASS TConversorModel

::resultado := if( ValType( cantidad ) == "N", cantidad * ::cambio, 0 )

return

//------------------------------------------------------------------
// Conversion from any coin to euro

PROCEDURE convDiv( cantidad ) CLASS TConversorModel

::resultado := if( ValType( cantidad ) == "N", cantidad / ::cambio, 0 )

return

//------------------------------------------------------------------
// Asign the conversion value

METHOD setCambio( cambio ) CLASS TConversorModel

local ret := ::cambio

if ValType( cambio ) == "N"
::cambio := cambio
end if

return ret

//------------------------------------------------------------------
// Get the conversion value

METHOD getCambio() CLASS TConversorModel
return ::cambio

//------------------------------------------------------------------

METHOD getResultado() CLASS TConversorModel
return ::resultado

//------------------------------------------------------------------

And this is is the specialized class to convert euros and pesetas — and old Spanish coin before the euro — and that inherits from the previous class:

//----------------------------------------------------------------//

#include "HbClass.ch"

//------------------------------------------------------------------
// Class to convert pesetas to Euros

CREATE CLASS TConversorModelEurosPesetas FROM TConversorModel

METHOD new() CONSTRUCTOR
METHOD deEurosAPesetas()
METHOD dePesetasAEuros()

END CLASS

//------------------------------------------------------------------
// Constructor

METHOD new() CLASS TConversorModelEurosPesetas

::setCambio( 166.386 )

return Self

//------------------------------------------------------------------
// Convert an amount to peseta

METHOD deEurosAPesetas( cantidad ) CLASS TConversorModelEurosPesetas

::convMul( cantidad )

return ::getResultado()

//------------------------------------------------------------------
// Convert an amount to euro

METHOD dePesetasAEuros( cantidad ) CLASS TConversorModelEurosPesetas

::convDiv( cantidad )

return ::getResultado()

//------------------------------------------------------------------

What is the view ?

We said that the model represents the information. This is not worth anything if you can’t see or manage it. That’s what the view is for.

The view can be the way to visualize one or more models in the screen. But it also could be a printed report or a PDF file or anything that show our data.

Here we could use classes or not. For example, in web programming, the view can be HTML pages and usually they are not classes, but they could be classes that include a header and a footer.

In our sample, I’m going to use classes. An abstract one and a specialized one.

The abstract class:

//----------------------------------------------------------------//

#include "HbClass.ch"

//------------------------------------------------------------------
// Abstract class for view
// In the inherit classes we have to implement the VIRTUAL methods
//------------------------------------------------------------------

CREATE CLASS TConversorVista

PROTECTED:
DATA tipoConversion // "Pesetas to Euros" -> 1 "Euros to Pesetas" -> 2

EXPORTED:
METHOD new() CONSTRUCTOR
// This methods will be implemented in every view
METHOD msg( cTxt, cTitulo ) VIRTUAL
METHOD muestraMenu() VIRTUAL
METHOD getCantidad() VIRTUAL
METHOD escribeCambio( s ) VIRTUAL
METHOD acercaDe() VIRTUAL
METHOD muestraFin() VIRTUAL

// SET GET
METHOD setTipoConversion( cTipo )
METHOD getTipoConversion()

END CLASS

//------------------------------------------------------------------
// Constructor

METHOD new() CLASS TConversorVista
return self

//------------------------------------------------------------------
// Set the conversion rate

METHOD setTipoConversion( cTipo ) CLASS TConversorVista

local ret := ::tipoConversion

if ValType( cTipo ) == 'C' .and. ( cTipo == '1' .or. cTipo == '2' )
::tipoConversion := cTipo
end if

return ret

//------------------------------------------------------------------
// Get the conversion rate

METHOD getTipoConversion() CLASS TConversorVista
return ::tipoConversion

//------------------------------------------------------------------

And this is the class from which we are going to instantiate our object seen in text format. I suggest you to create a class for your prefered GUI.

//----------------------------------------------------------------//

#include "HbClass.ch"

//------------------------------------------------------------------
// Definition of the VIEW class in text mode

CREATE CLASS TVistaTXT FROM TConversorVista

METHOD msg( cTxt, cTitulo )
METHOD leeOpcion()
METHOD muestraMenu()
METHOD operacionIncorrecta()
// Implementation of the methods of the view interface
METHOD escribeCambio()
METHOD getCantidad()
METHOD acercaDe()
METHOD muestraFin()

END CLASS

//------------------------------------------------------------------
// Show a message in the screen

METHOD msg( cMsg, cTitulo ) CLASS TVistaTXT

if ValType( cTitulo ) != 'C'
cTitulo := "Atencion"
endif

cTitulo := ";" + cTitulo + ";;;"

if ValType( cMsg ) != 'C'
cMsg := " "
endif

return Alert( cTitulo + cMsg )

//------------------------------------------------------------------
// Show the information in the screen

PROCEDURE escribeCambio( s ) CLASS TVistaTXT

::msg( s, "Resultado" )

return

//------------------------------------------------------------------
// Read the option from the screen

METHOD leeOpcion() CLASS TVistaTXT

local cOpcion := " "
local getList := {}

@ 10, 10 SAY "Elige opcion:" GET cOpcion

READ

return AllTrim( cOpcion )

//------------------------------------------------------------------
// Get the amount to convert

METHOD getCantidad() CLASS TVistaTXT

local nCantidad := 0
local getList := {}

@ 15, 10 SAY "Importe:" GET nCantidad

READ

// Limpia pantalla
@ 15, 10 SAY Space( 30 )

return nCantidad

//------------------------------------------------------------------
// Show the menu in the screen

PROCEDURE muestraMenu() CLASS TVistaTXT

cls

? " +----------------------------------------+"
? " | Indica operacion que quieres realizar: |"
? " +----------------------------------------+"
?
? " [1] De pesetas a euros"
? " [2] De euros a pesetas"
? " [3] Acerca de..."
? " [0] Salir"

return

//------------------------------------------------------------------
// Error message

PROCEDURE operacionIncorrecta() CLASS TVistaTXT

::msg( "Opcion incorrecta..." )

return

//------------------------------------------------------------------
// System information

PROCEDURE acercaDe() CLASS TVistaTXT

::msg( "Ejemplo en modo Texto;del;Patron de Diseño;MVC", "Acerca del ejemplo" )

return

//------------------------------------------------------------------
// Show the end of the execution

PROCEDURE muestraFin() CLASS TVistaTXT

cls

::msg( "Fin de la ejecucion" )

return

//------------------------------------------------------------------

As you can see in the code ot the four classes shown, none of them knows the existence of the others. The model doesn’t know about the view nor the controller, and the view doesn’t know about the model nor the controller.

What is the controller ?

As their name says, is who controls :-)

The controller is responsible for attending events and take care of the request of the user (in some systems it’s called actions that are a special kind of model). For this, it has to know the existence of the views and the models that have to be spread in our system.

It’s an agent between the user, the model and the view. There are different types of controllers. One of the most usual are the front controller that handles all request and take care of them. And ther can be also specialized controllers that only admits the request for with they was created or from a front controller.

Following with our sample, I’m going to create first an abstract class:

//------------------------------------------------------------------

#include "HbClass.ch"

//------------------------------------------------------------------
// Control 1/1

CREATE CLASS TConversorController

PROTECTED:
DATA vista
DATA modelo

EXPORTED:
METHOD new( vista, modelo ) CONSTRUCTOR
METHOD gestionDeTipoConversion( cTipo ) VIRTUAL
METHOD despachaAcciones()
METHOD fin()
// SET GET
METHOD getVista()
METHOD setVista( vista )
METHOD getModelo()
METHOD setModelo( modelo )

END CLASS

//------------------------------------------------------------------
// Constructor

METHOD new( vista, modelo ) CLASS TConversorController

::vista := vista
::modelo := modelo

return self

//------------------------------------------------------------------
// Handle the request

PROCEDURE despachaAcciones() CLASS TConversorController

local cTipo

while .t.

switch cTipo := ::vista:leeOpcion()

case '0'
::vista:muestraFin()
::fin()
exit

case '1'
case '2'
::vista:setTipoConversion( cTipo )
::gestionDeTipoConversion()
exit

case '3'
::vista:acercaDe()
exit

otherwise
::vista:operacionIncorrecta()

end switch
end

return

//------------------------------------------------------------------
// Run at the end

PROCEDURE fin() CLASS TConversorController

// Se haria todo lo del final
break

return

//------------------------------------------------------------------
// Get the view

METHOD getVista() CLASS TConversorController
return ::vista

//------------------------------------------------------------------
// Set the view

PROCEDURE setVista( vista ) CLASS TConversorController

::vista := vista

return

//------------------------------------------------------------------
// Get the model

METHOD getModelo() CLASS TConversorController
return ::modelo

//-----------------------------------------------------------------
// Set the model

PROCEDURE setModelo( modelo ) CLASS TConversorController

::modelo := modelo

return

//------------------------------------------------------------------

And now I’m going to code our specialized controller to convert between pesetas and euros:

//----------------------------------------------------------------//
/*
THE CONTROLLER
Here we receive the user request and route them to the model
*/

#include "HbClass.ch"

//------------------------------------------------------------------
// Controller

CREATE CLASS TConversorEurosPesetasController FROM TConversorController

METHOD gestionDeTipoConversion( cTipo )

END CLASS

//------------------------------------------------------------------
// Conversion control

PROCEDURE gestionDeTipoConversion() CLASS TConversorEurosPesetasController

local cantidad := ::vista:getCantidad()

switch ::vista:getTipoConversion()

case '1'
::vista:escribeCambio( hb_ntos( cantidad ) + " pesetas son: " + ;
hb_ntos( ::modelo:dePesetasAEuros( cantidad ) ) + " euros" )

exit

case '2'
::vista:escribeCambio( hb_ntos( cantidad ) + " euros son: " + ;
hb_ntos( ::modelo:deEurosAPesetas( cantidad ) ) + " pesetas" )

exit

otherwise
::vista:msg( "---< Se ha producido un ERROR >---" )

end switch

return

//------------------------------------------------------------------

Note in the code that neither in the model nor in the controller there is any output to the screen, this is done by the specialized class TVistaTXT. It should be nice that any reader will create a TVistaFWH class or a class for any other GUI and post it in the comments of the article. I let the challenge here ;-)

Now we need an starting point that will be our program. If you design it well, it don’t need to be coupled to your GUI:

//------------------------------------------------------------------

#include "hbclass.ch"

//------------------------------------------------------------------
// Main Program

PROCEDURE main()

local oAp := TAplicacion():new()

oAp:ejecuta()

return

//------------------------------------------------------------------
// Main class for our sample

CLASS TAplicacion

DATA controlador

METHOD new() CONSTRUCTOR
METHOD ejecuta()

END CLASS

//------------------------------------------------------------------
// Constructor

METHOD new() CLASS TAplicacion

local oVista := TVistaTXT():new()
local oModelo := TConversorModelEurosPesetas():new()

::controlador := TConversorEurosPesetasController():new( oVista, oModelo )

return self

//------------------------------------------------------------------
// Starting point

PROCEDURE ejecuta() CLASS TAplicacion

::controlador:getVista():muestraMenu()
::controlador:despachaAcciones()

return

//------------------------------------------------------------------

I hope that with this post we’ll start a discussion that promote this great publication that is Harbour magazine of our fried José Luis. If the article has a big reception, we can do other about design patterns as DAO, Facade, Adapter, Singleton or Decorator… to say some interesting ones.

I’m waiting for your comments…

Regards,

Manu Expósito

PD: All the source code is available at: https://github.com/JoseluisSanchez/MVC_Harbour

--

--