La programación de software por capas es una arquitectura en la que buscamos separar el código o lógica que hace tareas de negocios (facturar, ventas) de la lógica de presentación gráfica y de datos. También le conocer como modelo MCV, (vista, controlador, modelo).
La ventaja de este estilo es que el desarrollo es la facilidad de reutilización y mantenimiento, ya que en caso de algún cambio, solo se modifica la capa necesaria sin tener que revisar todo el código.
Capa lógica de presentación
Hace referencia a como se va a presentar la información del programa al usuario. El objetivo es separar todo aquellos que se muestra al usuario, esta capa no tiene conexión a base de datos, ni realizar operaciones de ningún tipo solo muestra datos en pantalla, la capa de presentación solicita mediante funciones que se ejecutan en la capa de la lógica de negocio.
Capa de lógica de negocio
Aquí es donde se encuentran las funciones clases y funciones o procedimientos que serán invocados a través de la interfaz gráfica.
Recibe peticiones o eventos del usuario, procesa esas peticiones y luego envía la respuesta a la interfaz gráfica, si es necesario esta capa se comunicara con la capa de datos, pero la capa de negocios no se conecta a la base de datos, solo recibe datos o los procesa. Aquí se ejecutan e invocan reglas o funciones de negocios por ejemplo, facturar, listar productos, etc.
Capa de datos
Aquí tendremos clases y funciones que se conectan a la base de datos y es donde se realizan transacciones con sql para leer, insertar, modificar o eliminar información en la base de datos.
Aquí ejecutaremos consultas sql de forma que ninguna de las otras capas saben donde esta la base de datos, así la capa de presentación podría estar en un pc y las otras capas en un servidor como servicio se software Saas.
A modo ejemplo realizaremos una aplicación en java con tres capas:
Estructura del proyecto Agencia de Autos
Utilizaremos Netbeans para el ejemplo pero puede aplicarse cualquier otro ide de programación para java. Creamos el proyecto con Netbeans, como una aplicación java que se denominará JAutos
Pulsa en la imágen para agrandarla
Luego los paquetes de java que serán las capas de presentación o aplicación, capa de datos y capa de negocios. Aunque aquí hablamos de tres capas, también podemos crear otras capas de servicios y recursos para otras clases, configuraciones, etc
Pulsa en la imágen para agrandarla
Tendremos así creadas las tres capas y el archivo Jautos.java que sera el que contiene el método main que es el punto de partida de cualquier programa java.
El inicio de la aplicación por defecto será el siguiente código.
package jautos;
public class JAutos { /** Metodo main de inicio */ public static void main(String[] args) { // TODO code application logic here } }
Comenzaremos ahora a programar las funcionalidades capa por capa. La aplicación tiene por finalidad gestionar un listado de vehículos donde poder insertar datos de automóviles y leerlos para luego mostrarlos en una tabla tipo Jtable. Para ello añadiremos otra capa que encapsule los datos de la clase Vehículos en otra capa de recursos, crearemos una cuarta capa que se denominara CapaRecursos y dentro la clase Vehiculos que contendrá el siguiente código.
package CapaRecursos; public class Vehiculos { private String marca; private String modelo; private String color; public void SetMarca(String marca) { this.marca=marca; } public String GetMarca() { return this.marca; } { this.marca=marca; } public void SetModelo(String modelo) { this.modelo=modelo; } public String GetModelo() { return this.modelo; } public void SetColor(String color) { this.color=color; } public String GetColor() { return this.color; } } }Definimos los tributos de la clase que sera marca, modelo y color del vehículo, podemos añadir mas si quiere, pero con esos pocos es mas fácil de entender el código. Crearemos ahora el diseño de pantalla en la capa aplicación, allí podremos un Jframe podemos insertarlo con el botón derecho le daremos el nombre de frmAutos.
Pulsa en la imágen para agrandarla
Ahora añadiremos los controles necesarios para nuestra aplicación:
Pulsa en la imágen para agrandarla
El código es el siguiente:
package CapaAplicacion; import CapaRecursos.Vehiculos /** * * @author alumno */ public class frmAutos extends javax.swing.JFrame { /** * Creates new form frmAutos */ public frmAutos() { initComponents(); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initComponents() { jLabel1 = new javax.swing.JLabel(); jScrollPane1 = new javax.swing.JScrollPane(); jTable1 = new javax.swing.JTable(); txtMarca = new javax.swing.JTextField(); txtModelo = new javax.swing.JTextField(); jLabel2 = new javax.swing.JLabel(); jLabel3 = new javax.swing.JLabel(); txtColor = new javax.swing.JTextField(); btRegistrar = new javax.swing.JButton(); btLeer = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); jLabel1.setText("Marca"); jTable1.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { {null, null, null, null}, {null, null, null, null}, {null, null, null, null}, {null, null, null, null} }, new String [] { "Title 1", "Title 2", "Title 3", "Title 4" } )); jScrollPane1.setViewportView(jTable1); txtMarca.setName("marca"); // NOI18N txtModelo.setToolTipText(""); txtModelo.setName("modelo"); // NOI18N txtModelo.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { txtModeloActionPerformed(evt); } }); jLabel2.setText("Modelo"); jLabel3.setText("Color"); txtColor.setToolTipText(""); btRegistrar.setText("Registrar"); btRegistrar.setActionCommand("btRegistrar"); btRegistrar.setName("btRegistrar"); // NOI18N btRegistrar.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btRegistrarActionPerformed(evt); } }); btLeer.setText("Leer"); btLeer.setToolTipText(""); btLeer.setName("btLeer"); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 375, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel1) .addComponent(jLabel2) .addComponent(jLabel3)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(txtModelo) .addComponent(txtMarca) .addComponent(txtColor, javax.swing.GroupLayout.DEFAULT_SIZE, 196, Short.MAX_VALUE)) .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(btRegistrar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(btLeer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel1) .addComponent(txtMarca, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(btRegistrar)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel2) .addComponent(txtModelo, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel3) .addComponent(txtColor, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGroup(layout.createSequentialGroup() .addGap(15, 15, 15) .addComponent(btLeer))) .addGap(26, 26, 26) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 181, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap(32, Short.MAX_VALUE)) ); pack(); }// </editor-fold> private void txtModeloActionPerformed(java.awt.event.ActionEvent evt) { // TODO add your handling code here: } private void btRegistrarActionPerformed(java.awt.event.ActionEvent evt) { } /** * @param args the command line arguments */ public static void main(String args[]) { /* Set the Nimbus look and feel */ //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html */ try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException ex) { java.util.logging.Logger.getLogger(frmAutos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (InstantiationException ex) { java.util.logging.Logger.getLogger(frmAutos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { java.util.logging.Logger.getLogger(frmAutos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(frmAutos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } //</editor-fold> /* Create and display the form */ java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new frmAutos().setVisible(true); } }); } // Variables declaration - do not modify private javax.swing.JButton btLeer; private javax.swing.JButton btRegistrar; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTable jTable1; private javax.swing.JTextField txtColor; private javax.swing.JTextField txtMarca; private javax.swing.JTextField txtModelo; // End of variables declaration }
Importamos la capa y su clase frmAutos, luego en el main indicamos que la variable frmvehiculos, es una instancia del formulario o pantalla frmAutos y por lo ttno se incie y muestre al ejecutarse la aplicación.
Pulsa en la imágen para agrandarla
Vemos como queda la aplicación al ejecutarse por primera vez con los controles y llamando a la capa de aplicación o presentación
Ahora debemos crear las demás capas.
Crearemos en la capa de datos el archivo DBAutos.java, aquí podríamos crear conexiones a bases de datos y consultas sql en este caso para hacer pruebas independientes de cualquier base de datos utilizaremos una matriz que luego podría ser grabada en cualquier base de datos o exportada a un archivo xml o lo que quisiéramos. Necesitaremos acceder a datos por lo tanto importamos las variables credas en la capa recursos con el código.
Import CapaRecursos.Vehiculos;Necesitaremos importar otros compoentes para manejar la tabla:
package CapaDatos; import CapaRecursos.Vehiculos; import java.util.ArrayList; import java.util.List; import javax.swing.JTable; import javax.swing.table.DefaultTableModel; /** * * @author alumno */ public class DBAutos { public static DefaultTableModel Registrar(Vehiculos vehiculo, JTable Jtable1) { DefaultTableModel modeloDeDatosTabla = (DefaultTableModel) Jtable1.getModel(); Object[] datosRegistro = { vehiculo.GetMarca(), vehiculo.GetModelo(), vehiculo.GetColor() }; modeloDeDatosTabla.addRow(datosRegistro); return modeloDeDatosTabla; } public static List<Vehiculos> LeerTodo(JTable lsVehiculos) { List<Vehiculos> listaVehiculo=new ArrayList<>(); for(int i=0;i<lsVehiculos.getRowCount();i++) { Vehiculos vehiculo=new Vehiculos(); vehiculo.SetMarca(lsVehiculos.getValueAt(i, 0).toString()); vehiculo.SetModelo(lsVehiculos.getValueAt(i, 1).toString()); vehiculo.SetColor(lsVehiculos.getValueAt(i, 2).toString()); listaVehiculo.add(vehiculo); } return listaVehiculo; } }
Creamos el método Registrar para registrar un vehículo y definimos cual es la estructura por default de la tabla. También creamos el método para leer los registro que ingresemos a la matriz.
Nos queda ahora implementar la capa de negocios. Para ello crearemos dentro del paquete Capa de negocios el archivo negocioautos.java que será la clase de la capa de negocios y quien tendrá la funcionalidad para intermediar entre la capa de presentacion o aplicación y la capa de datos.
El código de la capa de negocios es el siguiente:
package CapaNegocio; import CapaDatos.DBAutos; import CapaRecursos.Vehiculos; import java.util.ArrayList; import java.util.List; import javax.swing.JOptionPane; import javax.swing.JTable; /** * * @author alumno */ public class negocioautos { public void Registrar(Vehiculos vehiculo, JTable Jtable1) { try { Jtable1.setModel(DBAutos.Registrar(vehiculo, Jtable1)); } catch(Exception ex) { } } public List<Vehiculos> Leer(JTable tabla) { List<Vehiculos> listaAutos=new ArrayList<>(); try { listaAutos=DBAutos.LeerTodo(tabla); } catch(Exception ex) { } finally { return listaAutos; } } }
Queda demás para que todo funciones colocar en la pantalla de presentación dos botones uno para insertar datos y otro para leer datos que son las dos funciones que tenemos en la capa de negocios que consulta a la capa de datos.
Antes de colocar los botones debe asegurarnos de haber importado las capas de recursos y negocios, además de los componente para manejar la matriz de datos que en este caso cumple la función de base de datos, importamos en la capa de presentación o aplicación sino las tenemos añadidas anteriormente.
import CapaRecursos.Vehiculos; import CapaNegocio.negocioautos; import java.util.ArrayList; import java.util.List; import javax.swing.JTable; import javax.swing.table.DefaultTableModel;En el botón Registrar en el action performed escribimos el siguiente código:
private void btRegistrarActionPerformed(java.awt.event.ActionEvent evt) { Vehiculos auto=new Vehiculos(); auto.SetMarca(txtMarca.getText()); auto.SetModelo(txtModelo.getText()); auto.SetColor(txtColor.getText()); new negocioautos().Registrar(auto, jTable1); }
El Resultado al ejecutar será el siguiente:
Pulsa en la imágen para agrandarla
Al pulsar el botón Registrar en la pantalla de listado de autos se invoca al método new negocioautos().Registrar(auto, jTable1); de la capa de negocios y se le envía como parámetros los datos que se encuentran en la matriz autos y la tabla donde se mostraran los datos insertados.
Vamos a añadir otra tabla para clonar los datos de la primera mediante el botón leer. Para ello añadimos una Jtable debajo y los dejamos sin configurar sus propiedades ya que la modificaremos desde código. Si queremos inspeccionar como modificar los parámetros por defecto de una tabla, podemos verlos haciendo clic derecho sobre la tabla y luego en el menu vamos a la opcion propiedades allí podremos modificar distintas opciones como los titulos de las columnas, añadir o quitar columnas o filas, implementar distintos comportamientos al iniciar la aplicación.
Pulsa en la imágen para agrandarla
Veremos en el código como cambiar los nombres de las columnas y vaciar la tabla para que tome los datos de la tabla principal donde se introducen los datos. Luego en el action performed del botón leer escribimos el siguiente código para poder leer los datos y volcarlos completamente en otra tabla para demostrar el funcionamiento y como podemos clonar una tabla con los datos que vamos introduciendo en otra o bien incluso usarlo para mostrar en las cajas de texto o guardarlo datos en un archivo de backup o comparar datos con facilidad.
private void btLeerActionPerformed(java.awt.event.ActionEvent evt) { // TODO add your handling code here: List<Vehiculos> listaVehiculo=new negocioautos().Leer(jTable1); DefaultTableModel Tabla=new DefaultTableModel(); Tabla.addColumn("Marca"); Tabla.addColumn("Modelo"); Tabla.addColumn("color"); for(Vehiculos vehiculo:listaVehiculo) { Object[] registroLeido= { vehiculo.GetMarca(), vehiculo.GetModelo(), vehiculo.GetColor() }; Tabla.addRow(registroLeido); } jTable2.setModel(Tabla); }
Veamos como queda la funcionalidad con el botón leer y la segunda tabla añadida.
Pulsa en la imágen para agrandarla
Luego de cargar la tabla al pulsar el botón leer esta se clona en una segunda tabla si añadimos mas datos a la primera tabla y luego pulsamos leer se volverá a clonar la primer tabla sobre la segunda. De esta forma se puede ver como se ha separado en capas las distintas funcionalidades de la aplicación.
La programación en capas es una metodologia que permite trabajar con total libertad, no es una técnica rígida que debe implementarse de una manera estructurada. Los desarrolladores de software e incluso de proyectos web tienen múltiples formas de implementarlas según sus necesidades y poder reutilizar muchas librerías de código desarrollado en capas, se puede implementar una capa en otro proyecto. La tendencia a utilizar el modelo de programación en N capas en grande proyecto con varios equipos de desarrolladores y cuando se trata principalmente de aplicaciones empresariales donde
se deben manejar gran cantidad de subsistemas y módulos.
Algunas desventajas de este modelo de programación es cuando se implementa se debe llegar a un balance entre la cantidad capas, recursos, subsistemas o subcapas, clases y sus interrelaciones. Debemos documentar muy bien un desarrollo en capas y debe ser fácilmente comprensible suficiente para realizar un trabajo específico con eficiencia y ser lo más modular e independiente posible, para que un cambio no afecte a todo el sistema.
Dentro de las desventajas tenemos que analizar la pérdida de eficiencia si se comienza a hacer trabajo redundante o se programan varias capas que hacen lo mismo pero con distintos datos o conexiones constantes a la base de datos sin sentido que ralentizan la aplicación además de consultas sql mal optimizadas que pueden volver el software muy lento.
Se debe evitar también caer en el error de generar mucha dependencia entre los objetos, métodos, propiedades y atributos de cada clase y cada capa que justamente contradice el objetivo de la programación en 3 o N capas.
Algunas tecnologías actuales que soportan la Programación en Capas. En la actualidad, la mayoría de los programas hacen uso de .Java, PHP y Net.
La capa de datos se implementa mediante herramientas como Data Set y Data Reader. Muy similar a C# en .net
La otra herramienta muy utilizada en Java es Hibernate, que se utiliza para mapear bases de datos con objetos creados en Java algo como lo que vimos en el ejemplo con la clase vehiculos salvo que el mapeo lo hicimos construyendo la clase en forma manual.