Refactoreo de Una Librería Sencilla de Capa de Negocio
En esta oportunidad, analizaremos el refactoreo de una librería de Capa de Negocio utilizada para obtener datos desde una base de datos y cargarla en pantalla mediante el uso de una grilla. La librería, fue desarrollada de forma deliberada y a discresión. El refactoreo de esta librería nos permitirá comprender cómo se puede refinar el código y qué beneficios podemos obtener a cambio de ello. La idea central de este artículo es la de hacerlo reflexionar acerca de esta noble tarea de construir software de mejor calidad, aprovechando al máximo las virtudes del refinamiento y refactoreo del software. Mi mayor anhelo es despertar en Ud., la curiosidad de reflexión y, quien sabe, quizá contribuya a despertar su genialidad. Si logro ese acometido, me sentiré más que realizado.
Analizando el Código Original de la Librería
Analizando el Código Original de la Librería
A continuación dejo el código para que puedan conocer el estado orignal del mismo y como fue concebido este. La librería utiliza el proveedor de datos OleDB.
Imports System.Data
Imports System.Data.OleDb
Public Class DBTotalAccess
Private conn As OleDbConnection
Private dtAd As OleDbDataAdapter
Private dtSt As DataSet
Private _connectionString As String
Private _source As String
Public Property ConnectionString() As String
Get
Return _connectionString
End Get
Set(ByVal value As String)
_connectionString = value
End Set
End Property
Public Property Source() As String
Get
Return _source
End Get
Set(ByVal value As String)
_source = value
End Set
End Property
Public Sub New(ByVal setConnectionString As String, _
ByVal setSource As String)
_connectionString = setConnectionString
_source = setSource
End Sub
Private Function conectar() As OleDbConnection
Try
conn = New OleDbConnection
conn.ConnectionString = _connectionString
conn.Open()
Return conn
Catch ex As OleDb.OleDbException
Return Nothing
Catch ex As Exception
Return Nothing
End Try
End Function
Public Sub openDataBase(ByVal Grilla As DataGridView)
Try
dtAd = New OleDbDataAdapter(_source, _connectionString)
dtSt = New DataSet()
dtAd.Fill(dtSt, "Tabla")
Grilla.DataBindings.Clear()
Grilla.DataBindings.Add("DataSource", dtSt, "Tabla")
Catch ex As OleDb.OleDbException
Grilla.DataBindings.Clear()
Catch ex As Exception
Grilla.DataBindings.Clear()
End Try
End Sub
Protected Overrides Sub Finalize()
conn = Nothing
dtAd = Nothing
dtSt = Nothing
MyBase.Finalize()
End Sub
End Class
Imports System.Data.OleDb
Public Class DBTotalAccess
Private conn As OleDbConnection
Private dtAd As OleDbDataAdapter
Private dtSt As DataSet
Private _connectionString As String
Private _source As String
Public Property ConnectionString() As String
Get
Return _connectionString
End Get
Set(ByVal value As String)
_connectionString = value
End Set
End Property
Public Property Source() As String
Get
Return _source
End Get
Set(ByVal value As String)
_source = value
End Set
End Property
Public Sub New(ByVal setConnectionString As String, _
ByVal setSource As String)
_connectionString = setConnectionString
_source = setSource
End Sub
Private Function conectar() As OleDbConnection
Try
conn = New OleDbConnection
conn.ConnectionString = _connectionString
conn.Open()
Return conn
Catch ex As OleDb.OleDbException
Return Nothing
Catch ex As Exception
Return Nothing
End Try
End Function
Public Sub openDataBase(ByVal Grilla As DataGridView)
Try
dtAd = New OleDbDataAdapter(_source, _connectionString)
dtSt = New DataSet()
dtAd.Fill(dtSt, "Tabla")
Grilla.DataBindings.Clear()
Grilla.DataBindings.Add("DataSource", dtSt, "Tabla")
Catch ex As OleDb.OleDbException
Grilla.DataBindings.Clear()
Catch ex As Exception
Grilla.DataBindings.Clear()
End Try
End Sub
Protected Overrides Sub Finalize()
conn = Nothing
dtAd = Nothing
dtSt = Nothing
MyBase.Finalize()
End Sub
End Class
Cómo puede observar, la librería integra una serie de funciones, eventos, propiedades y procedimientos para su construcción. El código en aparencia tiene aspecto de ser escueto y de hecho lo es. Se puede afirmar que pese a su composición ha mantenido cierto grado de racionalidad en su construcción. Sin embargo, el rendimiento tiene sus puntos críticos que al final de este artículo señalare con una tabla de comparaciones. Ahora, debajo de este párrafo, muestro el refactoreo de esta librería.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Imports System.Data.OleDb
Public Interface IConnect
Function getConectar() As OleDbConnection
End Interface
Public Interface IConnect
Function getConectar() As OleDbConnection
End Interface
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Imports System.Data
Imports System.Data.OleDb
Public Class DBConnect
Implements IConnect
Private _conn As OleDbConnection
Private _connectionString As String
Public Sub setConnectionString(ByVal value As String)
_connectionString = value
End Sub
Public Function getConnectionString() As String
Return _connectionString
End Function
Public Sub New()
'Vacío Obligatorio.
End Sub
Public Sub New(ByVal cConnectionString As String)
_connectionString = cConnectionString
End Sub
Public Function getConectar() As System.Data.OleDb.OleDbConnection _
Implements IConnect.getConectar
Try
With _conn
.ConnectionString = _connectionString
.Open()
End With
Return _conn
Catch ex As OleDb.OleDbException
Return Nothing
Catch ex As Exception
Return Nothing
End Try
End Function
Protected Overrides Sub Finalize()
_conn = Nothing
MyBase.Finalize()
End Sub
End Class
Imports System.Data.OleDb
Public Class DBConnect
Implements IConnect
Private _conn As OleDbConnection
Private _connectionString As String
Public Sub setConnectionString(ByVal value As String)
_connectionString = value
End Sub
Public Function getConnectionString() As String
Return _connectionString
End Function
Public Sub New()
'Vacío Obligatorio.
End Sub
Public Sub New(ByVal cConnectionString As String)
_connectionString = cConnectionString
End Sub
Public Function getConectar() As System.Data.OleDb.OleDbConnection _
Implements IConnect.getConectar
Try
With _conn
.ConnectionString = _connectionString
.Open()
End With
Return _conn
Catch ex As OleDb.OleDbException
Return Nothing
Catch ex As Exception
Return Nothing
End Try
End Function
Protected Overrides Sub Finalize()
_conn = Nothing
MyBase.Finalize()
End Sub
End Class
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Imports System.Windows.Forms.DataGridView
Public Interface IDBStatic
Sub openDataBase(ByVal Grilla As DataGridView)
End Interface
Public Interface IDBStatic
Sub openDataBase(ByVal Grilla As DataGridView)
End Interface
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Imports System.Data
Imports System.Data.OleDb
Public Class DBStatic
Inherits DBConnect
Implements IDBStatic
Private _dtAd As OleDbDataAdapter
Private _dtSt As DataSet
Private _source As String
Public Sub setSource(ByVal value As String)
_source = value
End Sub
Public Function getSource() As String
Return _source
End Function
Public Sub New()
'Vacío Obligatorio.
End Sub
Public Sub New(ByVal cConnectionString As String)
MyBase.New(cConnectionString)
End Sub
Private Shared Sub cargarGrilla(ByVal Grilla As System.Windows.Forms.DataGridView, _
ByVal xDtSt As DataSet, _
ByVal xTabla As String)
With Grilla
.DataBindings.Clear()
.DataBindings.Add("DataSource", xDtSt, xTabla)
End With
End Sub
Private Shared Sub limpiarGrilla(ByVal setGrilla As System.Windows.Forms.DataGridView)
setGrilla.DataBindings.Clear()
End Sub
Public Sub openDataBase(ByVal setGrilla As System.Windows.Forms.DataGridView) _
Implements IDBStatic.openDataBase
Try
_dtAd = New OleDbDataAdapter(getSource(), getConnectionString())
_dtSt = New DataSet()
_dtAd.Fill(_dtSt, "Tabla")
cargarGrilla(setGrilla, _dtSt, "Tabla")
Catch ex As OleDb.OleDbException
limpiarGrilla(setGrilla)
Catch ex As Exception
limpiarGrilla(setGrilla)
End Try
End Sub
Protected Overrides Sub Finalize()
_dtAd = Nothing
_dtSt = Nothing
MyBase.Finalize()
End Sub
End Class
El refactoreo de esta librería presenta la construcción de dos clases y dos interfaces. Empezaremos analizando la clase e interfaz utilizada para manipular las conexiones.
La interfaz IConnect tan solo contiene una función llamada getConectar(). La clase llamada DBConnect contiene el código para la manipulación de las conexiones. La clase DBConnect implementa la interfaz IConnect. Notará que aquí también he omitido el particular proceso de encapsulación que se acostumbra utilizar en Visual Basic .NET, me refiero a los Property. En lugar de ello, he tomado la normalización que es muy utilizada en Java. El uso de procedimientos y funciones, a mi juicio, resultan más efectivas para estos casos. Sin embargo, existe una excepción a esta regla. Si se prevee la construcción de clases para a bases de datos orientada a objetos, típico en el lenguaje LINQ, quizá resulte más adecuado el uso de encapsulaciones de campos y propiedades utilizando los Property clásicos. Dado que este no es el caso, he decidido sujetarme a las normalizaciones típicas utilizadas en Java y que creo convenientes utilizar en Visual Basic .NET.
Dentro de la clase DBConnect he creado una función que es implementada desde la intefaz IConnect llamada getConectar() que permite la conexión hacia la base de datos. La ubicación y otros parámetros utiliza el conjunto de Setter & Getter llamado ConnectionString. A través de este conjunto de atributos, se le pasa la ubicación de la base de datos y todos sus parámetros que incluyen el tipo de proveedor, la versión, el tamaño del buffer, tipo de aislación, seguridad, etc. A continuación, muestro una típica cadena de conexión sencilla que bien podría servir de ejemplo en esta ocasión.
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\RentaCars\Agencia.mdb
Imports System.Data.OleDb
Public Class DBStatic
Inherits DBConnect
Implements IDBStatic
Private _dtAd As OleDbDataAdapter
Private _dtSt As DataSet
Private _source As String
Public Sub setSource(ByVal value As String)
_source = value
End Sub
Public Function getSource() As String
Return _source
End Function
Public Sub New()
'Vacío Obligatorio.
End Sub
Public Sub New(ByVal cConnectionString As String)
MyBase.New(cConnectionString)
End Sub
Private Shared Sub cargarGrilla(ByVal Grilla As System.Windows.Forms.DataGridView, _
ByVal xDtSt As DataSet, _
ByVal xTabla As String)
With Grilla
.DataBindings.Clear()
.DataBindings.Add("DataSource", xDtSt, xTabla)
End With
End Sub
Private Shared Sub limpiarGrilla(ByVal setGrilla As System.Windows.Forms.DataGridView)
setGrilla.DataBindings.Clear()
End Sub
Public Sub openDataBase(ByVal setGrilla As System.Windows.Forms.DataGridView) _
Implements IDBStatic.openDataBase
Try
_dtAd = New OleDbDataAdapter(getSource(), getConnectionString())
_dtSt = New DataSet()
_dtAd.Fill(_dtSt, "Tabla")
cargarGrilla(setGrilla, _dtSt, "Tabla")
Catch ex As OleDb.OleDbException
limpiarGrilla(setGrilla)
Catch ex As Exception
limpiarGrilla(setGrilla)
End Try
End Sub
Protected Overrides Sub Finalize()
_dtAd = Nothing
_dtSt = Nothing
MyBase.Finalize()
End Sub
End Class
El refactoreo de esta librería presenta la construcción de dos clases y dos interfaces. Empezaremos analizando la clase e interfaz utilizada para manipular las conexiones.
Dentro de la clase DBConnect he creado una función que es implementada desde la intefaz IConnect llamada getConectar() que permite la conexión hacia la base de datos. La ubicación y otros parámetros utiliza el conjunto de Setter & Getter llamado ConnectionString. A través de este conjunto de atributos, se le pasa la ubicación de la base de datos y todos sus parámetros que incluyen el tipo de proveedor, la versión, el tamaño del buffer, tipo de aislación, seguridad, etc. A continuación, muestro una típica cadena de conexión sencilla que bien podría servir de ejemplo en esta ocasión.
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\RentaCars\Agencia.mdb
Notará que esta cadena es extremadamente sencilla. Deseo puntualizar algo al respecto. Las cadenas de conexión pueden variar según el proveedor de datos e, incluso, el tipo de conexión que se desee hacer hacia la base de datos. Aquí expongo el ejemplo de una conexión hacia un gestor de base de datos basado en Microsoft Access. Sin embargo, podría utilizar cualquier tipo de proveedor de datos, siempre y cuando, utilice el proveedor adecuado. Ud., podría utilizar SQL Server y estas líneas quizá serían distintas. Es más, si utiliza MySQL Server, también cambiarán e incluso tendrá que referenciar las librerías adecuadas mediante la palabra clave Imports y el espacio de nombre correspondiente a dicho proveedor de base de datos.
A continuación, dejo una tabla con la mayoría de los proveedores de datos y sus cadenas de conexión específicas para cada una de estas soluciones.
Por último, la clase DBConnect proporciona un evento llamado Finalize(). Este evento resulta ser muy importante. Permite liberar de la memoria las referencias de todas las variables que han sido cargadas por la clase. En pocas palabras, contribuye a mejorar la liberación de los recursos. Cabe puntualizar que el evento Finalize() solo contribuye como un elemento más para la cooperación de liberación de recursos, aunque quien realmente está encargado de liberar todos los recursos de forma responsable y directa es GC "Garbage Collector" o Recolector de Basura.
He construido una interfaz llamada IDBStatic y una clase llamada DBStatic. Esta clase y su interfaz, tiene como objetivo, preparar el uso de sus funciones para el proceso de acceder hacia la base de datos y cargar los datos en pantalla a través de una grilla, mediante el uso del objeto DatagridView. La interfaz IDBStatic tan solo contiene un procedimiento con un parámetro formal llamado openDataBase(). En la clase DBStatic, podemos ver como se implementa este procedimiento. Además de todo ello, la clase DBStatic, hereda de la clase DBConnect. Esto me permite proporcionar las operaciones y atributos de la clase DBConnect para utilizarlos dentro de las funcionalidades de la clase DBStatic.
En la clase DBStatic, más precisamente, dentro del procedimiento openDataBase(), Ud., podrá hallar el llamado hacia otro procedimiento que se llama cargarGrilla() y que es de caracter privado. Este procedimiento llamado cargarGrilla() tan solo está disponible para uso exclusivo interno de la clase DBStatic. Dentro del mismo, podrá encontrar parte de la lógica que controla el comportamiento de carga hacia la grilla, además claro está, de pasarle parámetros tales como el DataSet y un nombre para la tabla que resulta optativo. Puede colocar cualquier nombre, pero debe haber al menos uno para que el proceso funcione satisfactoriamente.
Respecto a la separación en partes que mencione recientemente, este extracto de código permite mejorar la mantenibilidad. Al crear este extracto, la mantenibilidad subió un punto de forma positiva. Como seguramente recordará en planteos anteriores acerca de la ley maquivélica "divide y vencerás" que hice en artículos pasados sobre refactoreos, bueno pues bien, he aquí tiene un ejemplo conciso.
A continuación les dejo la tabla de comparación de ambos procesos de construcción. Seguramente, Ud., sacará las conclusiones por mi.
Tabla del Código Original
A continuación, dejo una tabla con la mayoría de los proveedores de datos y sus cadenas de conexión específicas para cada una de estas soluciones.
Tabla de Cadenas de Conexión para ConnectionString
| |
DB
|
ConnectionString
|
Access
|
Access ODBC Connection String Driver
{Microsoft Access Driver (*.mdb)};Dbq=C:\demo.mdb;Uid=Admin;Pwd=;
Access OLEDB Connection String Driver
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\directory\demo.mdb;User Id=admin;Password=;
|
DB2
|
DB2 ODBC Connection String
driver={IBM DB2 ODBC DRIVER};Database=demodb;hostname=myservername;port=myPortNum;protocol=TCPIP; uid=myusername; pwd=mypasswd
DB2 OLEDB Connection String
Provider=IBMDADB2;Database=demodeb;HOSTNAME=myservername;PROTOCOL=TCPIP;PORT=50000;uid=myusername;pwd=mypasswd;
|
DBase
|
DBase ODBC Connection String
Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=c:\directory;
DBase OLEDB Connection String
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\directory;Extended Properties=dBASE IV;User ID=Admin;Password=
|
Excel
|
Excel ODBC Connection String
Driver={Microsoft Excel Driver (*.xls)};DriverId=790;Dbq=C:\MyExcel.xls;DefaultDir=c:\directory;
Excel OLEDB Connection String
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;Extended Properties='"Excel 8.0;HDR=Yes;IMEX=1"'
|
Exchange
|
Exchange OLEDB Connection String
oConn.Provider = "EXOLEDB.DataSource" oConn.Open = "http://myServerName/myVirtualRootName"
|
Firebird
|
Firebird ODBC Connection String
DRIVER=Firebird/InterBase(r) driver;UID=SYSDBA;PWD=mypasswd;DBNAME=c:\directory\demo.fdb
Firebird OLEDB Connection String
User=SYSDBA;Password=mypasswd;Database=demo.fdb;DataSource=localhost;Port=3050;Dialect=3;Charset=NONE;Role=;Connection lifetime=15;Pooling=true;MinPoolSize=0;MaxPoolSize=50;Packet Size=8192;ServerType=0
|
FoxPro
|
FoxPro ODBC Connection String
Driver={Microsoft Visual FoxPro Driver};SourceType=DBC;SourceDB=c:\demo.dbc;Exclusive=No;NULL=NO;Collate=Machine;BACKGROUNDFETCH=NO;DELETED=NO
FoxPro OLEDB Connection String
Provider=vfpoledb.1;Data Source=c:\directory\demo.dbc;Collating Sequence=machine
|
Informix
|
Informix ODBC Connection String
Driver={Informix-CLI 2.5 (32 Bit)};Server=demoservername;Database=demodb;Uid=myusername;Pwd=mypasswd
Informix OLEDB Connection String
Provider=Ifxoledbc.2;User ID=myusername;password=mypasswd;Data Source=demodb@demoservername;Persist Security Info=true
|
MySQL
|
MySQL ODBC Connection String
DRIVER={MySQL ODBC 3.51 Driver};SERVER=myservername;PORT=3306;DATABASE=mydemodb; USER=myusername;PASSWORD=mypasswd;OPTION=3;
MySQL OLEDB Connection String
Provider=MySQLProv;Data Source=mydemodb;User Id=myusername;Password=mypasswd;
|
Oracle
|
Oracle ODBC Connection String
Driver={Microsoft ODBC for Oracle};Server=myservername;Uid=myusername;Pwd=mypassword;
Oracle OLEDB Connection String
Provider=msdaora;Data Source=mydemodb;User Id=myusername;Password=mypasswd;
Oracle .Net Connection String
Data Source=mydemodb;User Id=myusername;Password=mypasswd;Integrated Security=no;
|
SQL Server
|
SQL Server ODBC Connection String - Database Login
Driver={SQL Server};Server=myservername;Database=mydemodb;Uid=myusername;Pwd=mypasswd;
SQL Server ODBC Connection String - Trusted Connection
Driver={SQL Server};Server=mysername;Database=mydemodb;Trusted_Connection=yes;
SQL Server OLEDB Connection String - Database Login
Provider=sqloledb;Data Source=myservername;Initial Catalog=mydemodb;User Id=myusername;Password=mypasswd;
SQL Server OLEDB Connection String - Trusted Connection
Provider=sqloledb;Data Source=myservername;Initial Catalog=mydemodb;Integrated Security=SSPI;
SQL Server .Net Connection String - Database Login
Server=myservername;Database=mydemodb;User ID=myusername;Password=mypasswd;Trusted_Connection=False
SQL Server .Net Connection String - Trusted Connection
Server=myservername;Database=mydemodb;Integrated Security=SSPI;
|
Sybase
|
Sybase ODBC Connection String
Driver={SYBASE ASE ODBC Driver};Srvr=myservername;Uid=myusername;Pwd=mypasswd
Sybase OLEDB Connection String
Provider=Sybase.ASEOLEDBProvider;Server Name=myservername,5000;Initial Catalog=mydemodb;User Id=myusername;Password=mypassword
|
Por último, la clase DBConnect proporciona un evento llamado Finalize(). Este evento resulta ser muy importante. Permite liberar de la memoria las referencias de todas las variables que han sido cargadas por la clase. En pocas palabras, contribuye a mejorar la liberación de los recursos. Cabe puntualizar que el evento Finalize() solo contribuye como un elemento más para la cooperación de liberación de recursos, aunque quien realmente está encargado de liberar todos los recursos de forma responsable y directa es GC "Garbage Collector" o Recolector de Basura.
He construido una interfaz llamada IDBStatic y una clase llamada DBStatic. Esta clase y su interfaz, tiene como objetivo, preparar el uso de sus funciones para el proceso de acceder hacia la base de datos y cargar los datos en pantalla a través de una grilla, mediante el uso del objeto DatagridView. La interfaz IDBStatic tan solo contiene un procedimiento con un parámetro formal llamado openDataBase(). En la clase DBStatic, podemos ver como se implementa este procedimiento. Además de todo ello, la clase DBStatic, hereda de la clase DBConnect. Esto me permite proporcionar las operaciones y atributos de la clase DBConnect para utilizarlos dentro de las funcionalidades de la clase DBStatic.
En la clase DBStatic, más precisamente, dentro del procedimiento openDataBase(), Ud., podrá hallar el llamado hacia otro procedimiento que se llama cargarGrilla() y que es de caracter privado. Este procedimiento llamado cargarGrilla() tan solo está disponible para uso exclusivo interno de la clase DBStatic. Dentro del mismo, podrá encontrar parte de la lógica que controla el comportamiento de carga hacia la grilla, además claro está, de pasarle parámetros tales como el DataSet y un nombre para la tabla que resulta optativo. Puede colocar cualquier nombre, pero debe haber al menos uno para que el proceso funcione satisfactoriamente.
Respecto a la separación en partes que mencione recientemente, este extracto de código permite mejorar la mantenibilidad. Al crear este extracto, la mantenibilidad subió un punto de forma positiva. Como seguramente recordará en planteos anteriores acerca de la ley maquivélica "divide y vencerás" que hice en artículos pasados sobre refactoreos, bueno pues bien, he aquí tiene un ejemplo conciso.
Analizando Rendimientos
A continuación les dejo la tabla de comparación de ambos procesos de construcción. Seguramente, Ud., sacará las conclusiones por mi.
Tabla del Código Original
Mantenibilidad
|
74
|
Ciclo de Complejidad
|
25
|
Profundidad de Herencia
|
7
|
Acomplamiento de Clases
|
22
|
Líneas de Código
|
56
|
Tabla del Código Refactoreado
Mantenibilidad
|
87
|
Ciclo de Complejidad
|
33
|
Profundidad de Herencia
|
7
|
Acomplamiento de Clases
|
25
|
Líneas de Código
|
64
|
Muchas Gracias por su Atención.
Job Systems Solutions
No hay comentarios:
Publicar un comentario