Expandir Minimizar
Personas que lo han encontrado útil: 2 de 3 - Valorar este tema

Herencia de Formularios - Más allá de la herencia gráfica

Por Alejandro Andrés Brozzo

Contenido

 1. Introducción
 2. Herencia en la capa de Reglas de Negocio
 3. Herencia en la capa de Presentación
 4. Conclusión

1. Introducción

A esta altura ya casi todos los programadores conocen lo que es la Programación Orientada a Objetos (OOP, por sus siglas en inglés). Los que no, es hora de que empiecen a informarse; de lo contrario no sólo se pierden de sus innumerables ventajas, sino que pronto podrían quedar fuera del mercado.

2. Herencia en la capa de Reglas de Negocio

Uno de los puntos más atractivos de la OOP es la herencia: codificar la funcionalidad de un objeto y permitir crear otro objeto que pueda utilizar toda esa funcionalidad, agregándole un refinamiento mayor (es decir, especializando el objeto "hijo"). Esta característica, unida al polimorfismo (la habilidad que tienen los objetos a responder un pedido, cada uno a su manera), permite el ahorro del tiempo de programación, la limpieza y claridad del código, la facilidad de mantenimiento, etc. Un ejemplo de su aplicación puede ser el que muestra la Figura 1:

Bb972281.art283-img01-521x358(es-es,MSDN.10).gif
Figura 1. Volver al texto.

De esta forma podemos tener una colección de empleados y llamar al método para calcular el sueldo de cada uno de ellos. Todos los objetos podrán responder al pedido de calcularSueldo() pero, según el tipo de empleado que sea, podrá calcularlo de la manera que le corresponda. Sin embargo, al momento de solicitar que se calcule el sueldo, el solicitante no tendrá que saber a cuál de las especializaciones de empleados se le está pidiendo dicho cálculo. Dicho más técnicamente, una superclase define los elementos y la funcionalidad en común y cada subclase se especializa según el negocio lo necesite.

Pero utilizar las ventajas de la OOP sólo en las reglas de negocio (como es el caso del ejemplo) es ignorar su potencia; es ahorrar tiempo en un lugar para desperdiciarlo en otro...

3. Herencia en la capa de Presentación

Radiografía de la aplicación
Si bien el ejemplo que desarrollaremos está codificado en C# y la plataforma .NET, puede utilizarse cualquier lenguaje y plataforma orientada a objetos.

Asumiendo un trabajo prolijo, los sistemas que programamos están divididos en capas y todo lo que tiene que ver con la presentación está separado de las reglas de negocio. Así, estará entonces separada nuestra aplicación de ejemplo, que por ahora trabajará con WinForms; es decir, una aplicación de escritorio. Vale la aclaración de que todo nuestro ejemplo lo haremos muy simplificado, apenas unas pocas tablas, para no alejarnos de nuestro objetivo.

Para poder explotar al máximo la herencia vamos a estandarizar un poco la organización de nuestra base de datos. Utilizaremos procedimientos almacenados (SP, por sus siglas en inglés) para manipular los datos, tanto para actualizarlos como para recuperarlos. Así, por cada tabla generaremos un SP de inserción (p_nombreTabla_ins), uno de actualización (p_nombreTabla_upd) y uno de eliminación (p_nombreTabla_del). Además, utilizaremos otro SP para recuperar un registro específico (p_nombreTabla_selectUno) y para cuando necesitemos búsquedas en las que el usuario pueda ingresar filtros, generaremos un nuevo SP (p_nombreTabla_selectFiltrado). Por último, todas las tablas tendrán una clave primaria formada por un único campo, numérico, de nombre id.

A grandes rasgos, vamos a explicar la capa de Datos y la de Reglas de Negocio, ya que para este artículo el interés está en la capa de Presentación. La capa de Datos contendrá los procedimientos y funciones necesarios para llamar a cada uno de los SP antes mencionados. Recibirán como parámetros la tabla sobre la que tendrán que actuar, y un arreglo (array) que contendrá los parámetros con los cuales se llamará a los SP. Si lo deseas, puedes hacer que los procedimientos de insert, update y delete retornen un string con el mensaje de error que pueda haber ocurrido.

La capa de Reglas de Negocio va a estar formada por una superclase abstracta, llamada Entidad de la cual heredarán todas las demás clases: Película, Clientes, Localidades, etc., y su única propiedad será id.

Para enlazar ambas capas existirá una capa extra de mapeo, que será la encargada de conocer la relación existente entre cada clase y la o las tablas que represente. Esta capa estará formada por tantas clases como la capa de Reglas de Negocio, y al igual que en ésta existirá una superclase de la cual heredarán las demás, cuya única propiedad será id, pero que además contendrá casi toda la lógica de persistencia, dejando un mínimo para redefinir en cada subclase (una nueva muestra del poder de la herencia).

Hasta este punto, no hay más que la típica aplicación de herencia, complicada un poco por la separación en capas especializadas. Pero para ir realmente más allá, para sacarle todo el jugo a la OOP, vamos a aplicarla también en la capa de Presentación. Pero vayamos por partes ...

Capa de Presentación - ABM
Una forma de tratar los ABM podría ser una ventana con todos los datos del objeto al cuál se le desea realizar el ABM, más un conjunto de botones: Agregar, Modificar y Eliminar, para indicar la acción a realizar; Aceptar y Cancelar, para confirmar o deshacer dicha acción; y un botón Buscar que nos permitirá recuperar el objeto a modificar o eliminar según ciertos filtros que ingresemos (Ver Figura 2). La funcionalidad que realizará cada uno de estos botones es casi independiente del objeto para el cual se diseñó cada ventana, y es ése casi el que muchas veces nos lleva a repetir muchísimo código de ventana a ventana. Veamos cada uno de los botones y realicemos un pseudocódigo de su funcionalidad.

Bb972281.art283-img02-480x472(es-es,MSDN.10).gif
Figura 2: Ventana típica de ABM para un sistema de gestión. Volver al texto.

Agregar

  1. Deshabilitar la caja de texto que permite el ingreso del código del objeto sobre el cual se realiza el ABM (ya que la generación del id es automática), deshabilitar los botones Agregar, Modificar, Eliminar y Buscar ya que su funcionalidad no será necesaria por el momento, y habilitar los botones Aceptar y Cancelar.

  2. Limpiar los controles que puedan contener datos de objetos anteriores, y cargar los que correspondan con valores por defecto o predefinidas, de existir. Habilitar los controles para permitir el ingreso de datos(*).

Modificar

  1. Idem Agregar (1).

Vemos que si se desea, puede utilizarse el mismo botón para ambas acciones, pero como conceptualmente son funcionalidades distintas, preferimos mantenerlos separados.

Buscar

  1. Abrir la ventana de búsqueda correspondiente al objeto sobre el cual se realiza el ABM.

  2. Una vez recuperado el id del objeto con el que se trabajará,

    1. Crear un nuevo objeto de mapeo (objMap).

    2. objMap busca los datos del registro con el id recuperado.

    3. Crear un nuevo objeto a persistir (objABM).

    4. objMap asigna los valores del registro que recupero de la base de datos a las correspondientes propiedades de objABM(*).

    5. Asignar a los controles del formulario el contenido de las propiedades de objABM(*).

Eliminar

  1. Generar un nuevo objeto de mapeo (objMap).

  2. objMap ejecuta su propia función de eliminación, que se encarga de dar de baja (física o lógicamente) al objeto recuperado. Para esto sólo necesita su id.

  3. Destruir los objetos relacionados al ABM.

Cancelar

  1. Idem Buscar desde el punto 2-1, salvo que en 2-2 el objeto ya existe.

Aceptar

  1. Crear un nuevo objeto de mapeo (objMap).

  2. Crear un nuevo objeto a persistir (objABM).

  3. Cargar las propiedades de objABM con sus correspondientes valores del formulario(*).

  4. objMap ejecuta su función de grabación, pasando como parámetro a objABM.

  5. Destruir los objetos relacionados al ABM.

Caja de texto txtId (al presionar ENTER , al perder el foco , o al hacer clic en Buscar con un valor en txtId)

  1. Idem Buscar desde el punto 2-1.

Como vemos por estos algoritmos, son muy pocos los pasos que dependen de cada formulario específico: sólo aquellos pasos marcados con "*". Por lo tanto, podemos pensar que toda esta funcionalidad común se puede codificar en una única superclase de tipo formulario, y que distintas subclases la hereden y sólo se encarguen de redefinir la funcionalidad específica para los objetos que necesiten mostrar o persistir. Evidentemente, esto no es más que la idea básica de la OOP.

Nuestra superclase para ABM se vería entonces como se muestra en la Figura 3:

Bb972281.art283-img03-448x295(es-es,MSDN.10).gif
Figura 3: Así se ve nuestra superclase para ABM. Volver al texto.

Y parte de su código:

protected virtual void btnAgregar_Click(object sender, System.EventArgs e) {
    limpiarFormulario();
    txtId.Text = "";
    modoEditable();
    rglEntidad = null;
}
//
protected void btnModificar_Click(object sender, System.EventArgs e) {
    modoEditable();
}
//
protected void btnEliminar_Click(object sender, System.EventArgs e) {
    if (confirmar("¿Está seguro de que desea eliminar el registro?") == DialogResult.Yes) {
        string resultado = mapEntidad.Eliminar(Convert.ToUInt16(txtId.Text));
        if(resultado == "0") {
            limpiarFormulario();
            txtId.Text = "";
            modoEstatico(false);
        }
        else
            mostrarError(resultado);
    }
    //
}
//
private void btnAceptar_Click(object sender, System.EventArgs e) {
    string resultado = validar(); //verifica que todos los datos sean del tipo correcto, etc, pero NO valida 
reglas del negocio
    if(resultado == "0") {
        cargarObjetoReglas();
        resultado = rglEntidad.validar(); // <-- Esto también lo hace el propio objeto. Lo pongo igual para no 
acceder de nuevo a la base si cancela, para recuperar los datos
        if(resultado == "0") {
            resultado = mapEntidad.Grabar(ref rglEntidad);
            if(resultado == "0") {
                txtId.Text = rglEntidad.Id.ToString();
                modoEstatico(true);
                txtId.Focus();
            }
        }
    }
    //
    if(resultado != "0")
        mostrarError(resultado);
}
//
protected void btnCancelar_Click(object sender, System.EventArgs e) {
    if(txtId.Text != "") { //era una edición
        mostrarDatosObjeto();
    }
    else {
        limpiarFormulario();
    }
    //
    modoEstatico(rglEntidad != null);
    txtId.Focus();
}
//
private void btnBuscar_Click(object sender, System.EventArgs e) {
    if (txtId.Text != "") {
        recuperarObjeto();
    }
    else {
        nuevoFormularioBusqueda();
        long codigo = frmFormularioBusqueda.BuscarCodigo();
        if (codigo != -1) {
            txtId.Text = codigo.ToString();
            recuperarObjeto();
        }
    }
    txtId.Focus();
}
//
private void txtId_TextChanged(object sender, System.EventArgs e) {
    if (btnModificar.Enabled) { //si no hay un registro activo no hace falta hacer nada
        limpiarFormulario();
        modoEstatico(false);
    }
}
//
private void txtId_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) {
    if (e.KeyChar == 13 & txtId.Text != "") {
        recuperarObjeto();
        e.Handled = true; //para que no haga un beep cada vez que toma el <ENTER>
    }
}
//
protected void recuperarObjeto(){
    try {
        rglEntidad = mapEntidad.Recuperar(Convert.ToUInt16(txtId.Text));
        mostrarDatosObjeto();
        btnModificar.Enabled = true;
        btnEliminar.Enabled = true;
    }
    catch(Exception exError) {
        mostrarError(exError.Message);
    }
}
//
private void cargarObjetoRecuperado() {
    if(txtId.Text != "") {
        try {
            rglEntidad = mapEntidad.Recuperar(Convert.ToUInt16(txtId.Text));
            mostrarDatosObjeto();
        }
        catch(Exception exError) {
            limpiarFormulario();
            mostrarError(exError.Message);
        }
    }
    //
    modoEstatico(rglEntidad != null);
    //
}
//
protected void frmMoldeABM_Load(object sender, System.EventArgs e) {
    crearObjetoMapeo();
    modoEstatico(false);
}
 
protected void frmMoldeABM_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
    mapEntidad = null;
    rglEntidad = null;
}
//
//Para redefinir en cada formulario
//
protected virtual void modoEstatico(bool hayObjetoActivo) {
    txtId.Enabled = true;
    txtId.Focus();
    btnBuscar.Enabled = true;
    btnAgregar.Enabled = true;
    btnModificar.Enabled = hayObjetoActivo;
    btnEliminar.Enabled = hayObjetoActivo;
    btnAceptar.Enabled = false;
    btnCancelar.Enabled = false;
}
//
protected virtual void modoEditable() {
    txtId.Enabled = false;
    btnBuscar.Enabled = false;
    btnAgregar.Enabled = false;
    btnModificar.Enabled = false;
    btnEliminar.Enabled = false;
    btnAceptar.Enabled = true;
    btnCancelar.Enabled = true;
}
//
protected virtual void limpiarFormulario() {
}
//
protected virtual void mostrarDatosObjeto() {
}
//
protected virtual void cargarObjetoReglas() {
}
//
protected virtual void crearObjetoMapeo() {
}

La implementación de lo "particular" de cada formulario heredado se realiza por medio de la redefinición de los procesos que el formulario padre no puede conocer en detalle. Por ejemplo, para el ABM de Clientes se agregan varios controles (Ver Figura 4):

Bb972281.art283-img04-480x464(es-es,MSDN.10).gif
Figura 4. Volver al texto.

Y el único código extra para redefinir:

//
protected override void modoEstatico(bool hayObjetoActivo) {
    base.modoEstatico(hayObjetoActivo);
    txtNombre.Enabled = false;
    txtDireccion.Enabled = false;
    txtEmail.Enabled = false;
    txtTelefonos.Enabled = false;
    cboDiaNac.Enabled = false;
    cboMesNac.Enabled = false;
    txtAnoNac.Enabled = false;
    txtObservaciones.Enabled = false;
}
//
protected override void modoEditable() {
    base.modoEditable();
    txtNombre.Enabled = true;
    txtDireccion.Enabled = true;
    txtEmail.Enabled = true;
    txtTelefonos.Enabled = true;
    cboDiaNac.Enabled = true;
    cboMesNac.Enabled = true;
    txtAnoNac.Enabled = true;
    txtObservaciones.Enabled = true;
    txtNombre.Focus();
}
//
protected override void limpiarFormulario() {
    //txtId.Text = "";
    txtNombre.Text = "";
    txtDireccion.Text = "";
    txtEmail.Text = "";
    txtTelefonos.Text = "";
    cboDiaNac.SelectedIndex = -1;
    cboMesNac.SelectedIndex = -1;
    txtAnoNac.Text = "";
    txtObservaciones.Text = "";
}
//
protected override void mostrarDatosObjeto() {
    if(rglEntidad == null) {
        //### ??
    }
    else {
        //
        Reglas.Cliente rglAuxCliente = (Reglas.Cliente) rglEntidad;
        txtId.Text = rglAuxCliente.Id.ToString();
        txtNombre.Text = rglAuxCliente.Nombre;
        txtDireccion.Text = rglAuxCliente.Direccion;
        txtEmail.Text = rglAuxCliente.Email;
        txtTelefonos.Text = rglAuxCliente.Telefonos;
        cboDiaNac.SelectedIndex = Convert.ToInt32(rglAuxCliente.FechaNacimiento.ToString("dd")) - 1;
        cboMesNac.SelectedIndex = Convert.ToInt32(rglAuxCliente.FechaNacimiento.ToString("MM")) - 1;
        txtAnoNac.Text = rglAuxCliente.FechaNacimiento.ToString("yyyy");
        txtObservaciones.Text = rglAuxCliente.Observaciones;
        //
        rglAuxCliente = null;
        //
    }
}
//
protected override void cargarObjetoReglas() {
    //
    //rglEntidad = new Reglas.Cliente();
    //
    //Reglas.Cliente rglAuxCliente = (Reglas.Cliente) rglEntidad;
    Reglas.Cliente rglAuxCliente = new Reglas.Cliente();
    if(txtId.Text != "")
        rglAuxCliente.Id = Convert.ToUInt16(txtId.Text);
    rglAuxCliente.Nombre = txtNombre.Text;
    rglAuxCliente.Direccion = txtDireccion.Text;
    rglAuxCliente.Email = txtEmail.Text;
    rglAuxCliente.Telefonos = txtTelefonos.Text;
    rglAuxCliente.Observaciones = txtObservaciones.Text;
    try{
        rglAuxCliente.FechaNacimiento = DateTime.Parse(cboDiaNac.SelectedItem + "/" + 
cboMesNac.SelectedIndex + 1) + "/" + txtAnoNac.Text);
    }
    catch {
    }
    //
    rglEntidad = rglAuxCliente;
    rglAuxCliente = null;
    //
}
//
protected override void crearObjetoMapeo() {
    mapEntidad = new Mapeo.Cliente();
}
//

Como podemos ver, el código que agregamos para redefinir a la clase padre es mínimo, y está exclusivamente ligado a los controles nuevos de este formulario. Podemos hacer lo mismo con el resto de los formularios de ABM de la aplicación, e inclusive repetir la idea para los formularios de búsqueda, simplificando ampliamente la codificación y el mantenimiento de nuestro código.

Ampliando el horizonte

Con un poco más de dificultad, podemos también ir más allá: podemos aplicar la misma idea con Formularios Web, es decir, con aplicaciones pensadas para correr a través de internet o de una intranet. En ASP .NET, un formulario Web es una clase que se caracteriza por tener la parte visual declarada en lenguaje HTML, pero este lenguaje no es orientado a objetos, por lo tanto no soporta herencia. Para lidiar con este problema tendremos que repetir desde el formulario Web padre hacia el resto de los formularios hijos el código base que se encuentre en dicho lenguaje, y luego agregar los controles que necesitemos para cada formulario particular (tal cual lo hicimos para WinForms). Luego, debemos modificar el code-behind del formulario indicando que éste herede de nuestro formulario padre, y eliminar los controles que automáticamente agregó el diseñador pero que en realidad estaremos heredando. El resto del formulario Web se trabaja de la misma manera en que lo hicimos con los formularios para aplicaciones Windows.

Así, tendremos un formulario padre tal como se lo muestra en la Figura 5:

Bb972281.art283-img05-459x209(es-es,MSDN.10).gif
Figura 5. Volver al texto.

Representado por el siguiente código HTML:

<HTML>
  <HEAD>
    <title>Molde para ABMs</title>
    <meta name="vs_showGrid" content="True">
    <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" Content="C#">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema" content = "http://schemas.microsoft.com/intellisense/ie5">
    <script language="javascript" src="JavaScripts.js"></script>
    <LINK href="ssEstilo.css" type="text/css" rel="stylesheet">
  </HEAD>
 
  <body MS_POSITIONING="GridLayout">
    <form id="wfrmABMMolde" method="post" runat="server">
      <DIV style="DISPLAY: inline; Z-INDEX: 110; LEFT: 112px; WIDTH: 20px; POSITION: absolute; TOP: 2px; HEIGHT: 
19px; TEXT-ALIGN: right" align="right" ms_positioning="FlowLayout">Id:</DIV>
      <asp:TextBox id="txtAuxBusqueda" style="Z-INDEX: 101; LEFT: 152px; POSITION: absolute; TOP: 32px" 
runat="server" Width="32px"></asp:TextBox>
      <asp:textbox id="txtId" style="Z-INDEX: 102; LEFT: 144px; POSITION: absolute; TOP: 32px" tabIndex="1" 
runat="server" Width="56px"></asp:textbox>
      <asp:button id="btnBuscar" style="Z-INDEX: 103; LEFT: 208px; POSITION: absolute; TOP: 32px" tabIndex="2" 
runat="server" Text="Buscar" EnableViewState="False"></asp:button>
      <asp:button id="btnAgregar" style="Z-INDEX: 104; LEFT: 40px; POSITION: absolute; TOP: 168px" tabIndex="3" 
runat="server" Width="72px" Text="Agregar"></asp:button>
      <asp:button id="btnModificar" style="Z-INDEX: 105; LEFT: 144px; POSITION: absolute; TOP: 168px" 
tabIndex="4" runat="server" Width="72px" Text="Modificar"></asp:button>
      <asp:button id="btnEliminar" style="Z-INDEX: 107; LEFT: 248px; POSITION: absolute; TOP: 168px" 
tabIndex="5" runat="server" Width="72px" Text="Eliminar"></asp:button>
      <asp:button id="btnAceptar" style="Z-INDEX: 109; LEFT: 360px; POSITION: absolute; TOP: 64px" tabIndex="6" 
runat="server" Width="72px" Text="Aceptar"></asp:button>
      <asp:button id="btnCancelar" style="Z-INDEX: 106; LEFT: 360px; POSITION: absolute; TOP: 104px" 
tabIndex="7" runat="server" Width="72px" Text="Cancelar"></asp:button>
    </form>
  </body>
</HTML>

Para crear un formulario hijo que herede de éste, debemos copiar su código HTML y luego agregar las etiquetas que representen los nuevos controles:

<HTML>
  <HEAD>
    <title>ABM de Clientes</title>
    <meta name="vs_showGrid" content="True">
    <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" Content="C#">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema" content = "http://schemas.microsoft.com/intellisense/ie5">
    <script language="javascript" src="JavaScripts.js"></script>
    <LINK href="ssEstilo.css" type="text/css" rel="stylesheet">
  </HEAD>
 
  <body MS_POSITIONING="GridLayout">
    <form id="wfrmABMClientes" method="post" runat="server">
      <DIV style="DISPLAY: inline; Z-INDEX: 125; LEFT: 88px; WIDTH: 20px; POSITION: absolute; TOP: 35px; HEIGHT: 
19px; TEXT-ALIGN: right" align="right" ms_positioning="FlowLayout">Id:</DIV>
      <asp:TextBox id="txtAuxBusqueda" style="Z-INDEX: 101; LEFT: 128px; POSITION: absolute; TOP: 32px" 
runat="server" Width="32px"></asp:TextBox>
      <asp:textbox id="txtId" style="Z-INDEX: 102; LEFT: 120px; POSITION: absolute; TOP: 32px" tabIndex="1" 
runat="server" Width="56px"></asp:textbox>
      <asp:button id="btnBuscar" style="Z-INDEX: 103; LEFT: 184px; POSITION: absolute; TOP: 32px" tabIndex="2" 
runat="server" Text="Buscar" EnableViewState="False"></asp:button>
      <asp:button id="btnAgregar" style="Z-INDEX: 104; LEFT: 88px; POSITION: absolute; TOP: 400px" tabIndex="3" 
runat="server" Width="72px" Text="Agregar"></asp:button>
      <asp:button id="btnModificar" style="Z-INDEX: 105; LEFT: 192px; POSITION: absolute; TOP: 400px" 
tabIndex="4" runat="server" Width="72px" Text="Modificar"></asp:button>
      <asp:button id="btnEliminar" style="Z-INDEX: 107; LEFT: 296px; POSITION: absolute; TOP: 400px" 
tabIndex="5" runat="server" Width="72px" Text="Eliminar"></asp:button>
      <asp:button id="btnAceptar" style="Z-INDEX: 109; LEFT: 400px; POSITION: absolute; TOP: 120px" tabIndex="6" 
runat="server" Width="72px" Text="Aceptar"></asp:button>
      <asp:button id="btnCancelar" style="Z-INDEX: 106; LEFT: 400px; POSITION: absolute; TOP: 160px" 
tabIndex="7" runat="server" Width="72px" Text="Cancelar"></asp:button>
 
      <!—A partir de este punto, los controles agregados ->

      <DIV style="DISPLAY: inline; Z-INDEX: 141; LEFT: 0px; WIDTH: 86px; POSITION: absolute; TOP: 304px; HEIGHT: 19px" 
align="right" ms_positioning="FlowLayout">Observaciones:</DIV>
      <asp:TextBox id="txtObservaciones" style="Z-INDEX: 142; LEFT: 120px; POSITION: absolute; TOP: 304px" 
runat="server" Width="240px" Height="56px" tabIndex="15"></asp:TextBox>
      <DIV style="DISPLAY: inline; Z-INDEX: 143; LEFT: 40px; WIDTH: 71px; POSITION: absolute; TOP: 266px; 
HEIGHT: 19px" align="right" ms_positioning="FlowLayout">Fec. Nac.:</DIV>
      <DIV style="DISPLAY: inline; Z-INDEX: 144; LEFT: 298px; WIDTH: 6px; POSITION: absolute; TOP: 264px; 
HEIGHT: 19px" align="right" ms_positioning="FlowLayout">/</DIV>
      <asp:TextBox id="txtAnoNac" style="Z-INDEX: 145; LEFT: 312px; POSITION: absolute; TOP: 264px" 
runat="server" Width="48px" Height="20px" tabIndex="14"></asp:TextBox>
      <asp:DropDownList id="cboMesNac" style="Z-INDEX: 146; LEFT: 188px; POSITION: absolute; TOP: 264px" 
runat="server" Width="104px" Height="21px" tabIndex="13">
        <asp:ListItem Value="1">Enero</asp:ListItem>
        <asp:ListItem Value="2">Febrero</asp:ListItem>
        <asp:ListItem Value="3">Marzo</asp:ListItem>
        ...
        <asp:ListItem Value="11">Noviembre</asp:ListItem>
        <asp:ListItem Value="12">Diciembre</asp:ListItem>
      </asp:DropDownList>
      <asp:DropDownList id="cboDiaNac" style="Z-INDEX: 147; LEFT: 120px; POSITION: absolute; TOP: 264px" 
runat="server" Width="48px" Height="21px" tabIndex="12">
        <asp:ListItem Value="1">1</asp:ListItem>
        <asp:ListItem Value="2">2</asp:ListItem>
        <asp:ListItem Value="3">3</asp:ListItem>
        ...
        <asp:ListItem Value="30">30</asp:ListItem>
        <asp:ListItem Value="31">31</asp:ListItem>
        <asp:ListItem></asp:ListItem>
      </asp:DropDownList>
      <DIV style="DISPLAY: inline; Z-INDEX: 148; LEFT: 40px; WIDTH: 47px; POSITION: absolute; TOP: 229px; 
HEIGHT: 19px" align="right" ms_positioning="FlowLayout">Teléfonos:</DIV>
      <asp:TextBox id="txtTelefonos" style="Z-INDEX: 149; LEFT: 120px; POSITION: absolute; TOP: 227px" 
runat="server" Width="240px" Height="20px" tabIndex="11"></asp:TextBox>
      <asp:TextBox id="txtEmail" style="Z-INDEX: 150; LEFT: 120px; POSITION: absolute; TOP: 190px" 
runat="server" Width="240px" Height="20px" tabIndex="10"></asp:TextBox>
      <DIV style="DISPLAY: inline; Z-INDEX: 151; LEFT: 56px; WIDTH: 55px; POSITION: absolute; TOP: 190px; 
HEIGHT: 22px" align="right" ms_positioning="FlowLayout">E-mail:</DIV>
      <asp:TextBox id="txtNombre" style="Z-INDEX: 152; LEFT: 120px; POSITION: absolute; TOP: 80px" 
runat="server" Width="240px" Height="20px" tabIndex="8"></asp:TextBox>
      <DIV style="DISPLAY: inline; Z-INDEX: 153; LEFT: 40px; WIDTH: 62px; POSITION: absolute; TOP: 117px; 
HEIGHT: 19px" align="right" ms_positioning="FlowLayout">Dirección:</DIV>
      <asp:TextBox id="txtDireccion" style="Z-INDEX: 154; LEFT: 120px; POSITION: absolute; TOP: 117px" 
runat="server" Width="240px" Height="56px" tabIndex="9"></asp:TextBox>
      <DIV style="DISPLAY: inline; Z-INDEX: 155; LEFT: 48px; WIDTH: 55px; POSITION: absolute; TOP: 82px; HEIGHT: 
19px" align="right" ms_positioning="FlowLayout">Nombre:</DIV>
      <DIV style="DISPLAY: inline; Z-INDEX: 156; LEFT: 174px; WIDTH: 6px; POSITION: absolute; TOP: 264px; 
HEIGHT: 19px" align="right" ms_positioning="FlowLayout">/</DIV>
    </form>
  </body>
</HTML>

Al momento de codificar la funcionalidad de la clase formulario, ya podemos utilizar la herencia y todas las demás funcionalidades que nos ofrece la OOP, tal como hicimos con los formularios windows.

4. Conclusión

La utilización de la herencia al momento de codificar una aplicación, ya sea Windows o Web, simplifica y reduce el trabajo del programador, y su extensión a la capa de presentación profundiza y explota aún más sus beneficios. No sólo permite reducir la cantidad de trabajo y facilita el mantenimiento (las características más importantes de la OOP), sino que además brinda una uniformidad visual y una estandarización que de otra manera deberíamos manejar manualmente; tarea que se complica en grupos de trabajo de cualquier tamaño.

Quienes migran de un lenguaje procedural a un lenguaje orientado a objetos (como puede ser de Visual Basic 6 a Visual Basic .NET) suelen aprender a aprovechar sus beneficios, pero también es común que pierdan, por desconocimiento o falta de costumbre, características como las presentadas en este artículo.

Bb972281.Alejandro_Andres_Brozzo(es-es,MSDN.10).gifAlejandro Andrés Brozzo es desarrollador independiente en C#, VB .NET, y ASP .NET; es además Ingeniero en Sistemas de Información de la Universidad Tecnológica Nacional, Facultad Regional Rosario, Argentina.
¿Te ha resultado útil?
(Caracteres restantes: 1500)
© 2013 Microsoft. Reservados todos los derechos.