La paginación de resultados es el ejemplo más interesante y práctico que podemos desarrollar usando esta metodología. Todos los sitios que implementan algún tipo de búsqueda en algún momento usan la paginación resultados para no mostrar una calidad desmesurada de datos en una página.
Como podemos ver en Hotmail o yahoo, se muestras los email en varias páginas diferentes poniendo debajo un botón siguiente o anterior para mostrar las demás páginas.
Además podemos seleccionar un correo que se esta mostrando y borrarlo.
Ejecuta el
codigo de este ejemplo (http://flechado.com/Herramientas/controlventanas.aspx)
Indice |
Los requerimientos funcionales definen que hace la aplicación para el usuario. Cuando definimos los requerimientos externos la aplicación es vista como una caja negra, solo las características externas del sistema son
considerados.
El propósito principal de la especificación es definir las necesidades del usuario de la aplicación. Un cuidadoso análisis nos da un claro entendimiento de los requerimientos para suprimir posibles errores. La especificación es usada como referencia durante la verificación de la aplicación. La meta de la implementación es construir un producto que satisfaga la especificación. También es importante el uso de la especificación para entender claramente el impacto de los cambios y realizarlos de una manera confiable.
El modelo de caso de uso describe los requerimientos funcionales de la aplicación en términos de actores y casos de uso. Cada caso de uso define el comportamiento de algún aspecto de la aplicación sin considerar la estructura interna.
Casos de uso
A- Nombre del caso de uso: mostrar los primeros n registros.
Resumen: el sistema muestra la pagina con lo primeros n
registros
Actor: usuario de la aplicación
Descripción:
1- el usuario escribe en la barra de dirección de la pagina el nombre de la aplicación.
2- el browser se comunica en el servidor
3- el servidor solicita los primeros n registros al motor de base de datos
4- la base de datos busca los primeros n registros y se lo entrega al servidor
5- el servidor envía al browser los registros
6- el browser muestra los registros y señala que es la primera pagina.
B- Nombre del Caso de Uso: mostrar los siguientes registros
Pre-condición: la aplicación esta mostrando la pagina de los registros
Resumen: la aplicación muestra los siguientes n registros
Actor: usuario de la aplicación
1- el usuario presiona el botón siguiente
2- el browser envía al servidor el evento siguiente
3- el servidor solicita a la base de datos los siguientes n registros a partir del ultimo que se estaba mostrando
4- la base de datos entrega al servidor los registros que encuentra
5- si la base de datos entrega algún registro el servidor incrementa al numero de pagina
6- el servidor envía al browser los registros y el numero de pagina
7- el browser muestra los registros y el numero de pagina
C- Nombre del Caso de Uso: mostrar los registros anteriores
Similar al mostrar los siguientes registros
D- Nombre del Caso de Uso: borrar alguno de los registros que se
están mostrando
Pre-condicion: la aplicación esta mostrando algunos registros
Resumen: la aplicación borra los registros seleccionados
Actor: usuario de la aplicación
Descripción:
1- el usuario selecciona alguno de los registros que se están mostrando y presiona borrar
2- el browse envía los registros al servidor
3- el servidor busca los registros que están seleccionados para borrar y se los
envía a la base de datos
4- la base de datos borra los registros que le envió el servidor
5- el servidor solicita a la base de datos los siguientes n registro a partir del primero registro que estaba mostrando
6- la base de datos busca los registros y se lo envía al servidor
7- el servidor envía al browser los registros y el numero de pagina.
8- el browser muestra los registros y el numero de pagina.
Del análisis de los casos de uso encontramos que necesitaremos conocer el numero de pagina, el primer y ultimo registro que se esta mostrando.
El diseño de software es una fase fundamental en el proceso de transformación progresiva de la especificación, a través de un número intermedio de pasos, al producto final. En esta fase el software se descomposición en clases- describiendo que hace clase y su relación con las otras. Cada nuevo paso implementa los requerimientos identificados en los previos, el paso final es la implementación, que completa la transformación de la arquitectura del software en programas.
Describiremos la interacción entre los objetos con un diagrama de colaboración. El diagrama de colaboración esta basado en los casos de uso desarrollados en la fase de especificación. Para cada caso de uso necesitamos determinar los objetos que participan en el caso y mostrar como estos objetos interactúan entre si para satisfacer las necesidades descriptas en el caso de uso. Un diagrama de colaboración es desarrollado para cada caso de uso, los objetos que participan en el caso de uso son descriptos. Algunos objetos pueden aparecer en un solo diagrama de colaboración, mientras que otros pueden aparecer en varios. En un diagrama de colaboración, la secuencia en que los objetos participan en cada caso de uso es descripta por mensajes numerados. La secuencia de mensaje en el diagrama de colaboración puede corresponder a la secuencia de interacción entre el actor y la aplicación descripta en el caso de uso.
El diagrama de clases pude ser hecho del diagrama de colaboración. Es fácil determinar la operación del modelo de colaboración. Esto es porque el modelo de colaboración muestra los mensajes y la interacción entre los objetos, y el objeto que envía el mensaje invoca la operación del destinatario del objeto. Los mensajes pasados entre objetos pasivos consisten se traduce como un objeto invoca una operación provista por otro objeto.
A- Coordinator Objects. WebServer
Esta clase oculta los detalles de concurrencia, secuenciación y la lógica de la aplicación. Este objeto activo (tarea) describe como la aplicación responde a los eventos de entrada que recibe y las operaciones que invoca sobre los objetos.
B- Wrapper DataBase. Registros
Esta clase provee una internas a la base de datos, que puede usar el modelo relacional. Oculta los detalle de
la base de dato y todas las sentencias SQL, que nos permite abstraernos del motor de base de datos usado. En el ejemplo usaremos sql-server.
C- Data Abstraction Estado
Oculta los detalles internos de cómo son representados o implementados. Solo pueden ser accedidos por las operaciones provistas por la clase. Nos permite abstraernos de cómo se almacena el estado, puede ser DataView, session (Session, Application),u oculto en un campo de la pagina (htmlInputHidden).
D- User Interface. Browser
Esta provee una interfaz virtual que oculta los detalles del html. El impacto de los cambios del html esta limitado a la implementación de las operaciones de esta clase. Esta clase nos abstrae de todo el código html usado para mostrar la página en el browser.
Una tarea es usada para referirnos a un objeto activo con un hilo de control
Un importante objetivo en la estructuración en tareas y clase es la separación de intereses.
Una clase oculta información estática de diferentes clases: como abstracción de datos, interfaz de entrada, abstracción de la base de datos.
Una tarea oculta información concurrencia, en particular el control, la secuenciación y la comunicación.
En el modelo de análisis, la aplicación es representada como una colección de objetos que se comunican por mensajes. Durante la fase de estructuración de tareas, la concurrencia natural de la aplicación es formalizada, definiendo las tareas concurrentes y la interfaz de comunicación/sincronización entre estas.
La secuencia de eventos de la tarea describe como la tarea responde a cada uno de los menajes o eventos de entrada, y la salida generada como resultado .
Recibe (mensaje) de browser;
Extrae el nombre del mensaje y los parámetros
Case message of
message type ‘!IsPostBack':
// mostrar los primeros registros
// solicita a la base de datos los siguientes registros
registrosTemp =
RegistrosWrapperDataBase.Siguientes(new System.DateTime(2010, 07, 28, 22, 35, 5, 15));
if(registrosTemp.Count!=0) {
DataAbstractionState.PrimerRegistro=((int)dataView[0][0]);
DataAbstractionState.UltimoRegistro=((int)dataView[dataView.Count-1][0]);
registros=registrosTemp;
}
DataAbstraction.NumeroDePagina.Primera ();
UserInterfaceBrowser.Mostrar (registros, DataAbstractionNumeroDePagina);
DataBaseWrapperDataBase.Close();
message type ‘Siguientes':
// realiza el caso de uso siguiente
registrosTemp =
RegistrosWrapperDataBase.Siguientes (DataAbstractionState.UltimoRegistro);
if(registrosTemp.Count!=0) {
registros=registersTemp;
DataAbstractionState.PrimerRegistro=((int)dataView[0][0]);
DataAbstractionState.UltimoRegistro=((int)dataView[registers.Count-1][0]);
DataAbstractionNumeroDePagina.Siguiente ();
}
UserInterfaceBrowser.Mostrar (registros, DataAbstractionNumeroDePagina);
message type ‘Anteriores':
// realiza el caso de uso anterior
// similar a siguientes
message type ‘Borrar':
// realiza el caso de uso borrar
int registroId;
registroId= UserInterfaceBrowser.Borrar ();
string resultadoDb;
while(registroId!=-1) {
dataBase.Borrar (registroId);
resultadoDb= DataBaseWrapperDataBase.ExecuteBorrar ();
registroId= UserInterfaceBrowser.Borrar ();
}
registriesTemp =
RegistrosWrapperDataBase.MayorOIgual (DataAbstractionState.UltimoRegistro);
System.Data.DataView registriesTemp;
if(registrosTemp.Count!=0 ) {
registros=registrosTemp;
DataAbstractionState.PrimerRegistro=((int)dataView[0][0]);
DataAbstractionState.UltimoRegistro=((int)dataView[registers.Count-1][0]);
DataAbstractionNumeroDePagina.Siguiente ();
}
UserInterfaceBrowser.Mostrar (registros, DataAbstractionNumeroDePagina);
End case
El grado de conexión de una clase con otra. Altos niveles de acoplamiento entre las clases implica que las clases son difíciles de entender, modificar o cambiar, pues un cambio en una afecta a las otras. El más alto grado de cohesión es de datos, esto es si la comunicación entre las clases en solo pasando parámetros y estos parámetros son relevantes como un todo. Esto minimiza el efecto de los cambios debido a la propagación.
Las clases tienen un alto grado de cohesión pues la clase database y database pueden ser interpretadas como la implementación de un tipo de datos abstracto. Representan una abstracción de datos por ejemplo, un solo concepto semántica. Además si interpretamos el conjunto de variables de la clase como un esquema de relación están en tercera forma normal. Las clases con alta cohesión son fáciles de mantener y rehusar.
A- Coordinator Objects WebServer
namespace CoordinatorObjects {
public class WebServer:System.Web.UI.Page {
protected override void OnLoad(System.EventArgs e) {
DataBaseWrapper.DataBase dataBase;
DataAbstraction.Estado mostrando;
DataAbstraction.NumeroDePagina numeroDePagina;
System.Data.DataView registros;
UserInterface.Browser userInterfaceBrowser;
mostrando=new DataAbstraction.Estado (myPage);
dataBase=new DataBaseWrapper.DataBase();
numeroDePagina=new DataAbstraction.NumeroDePagina(this.Page);
registros=new System.Data.DataView();
userInterfaceBrowser=new UserInterface.Browser(this.Page);
registers=null;
//El servidor recibe los eventos del browser
//realiza el caso de uso mostrar primeros registros
if(!IsPostBack) {
mostrando.PrimerRegistro=System.Convert.ToString(new System.DateTime(2010, 07, 28, 22, 35, 5, 15));
mostrando.UltimoRegistro=System.Convert.ToString(new System.DateTime(2010, 07, 28, 22, 35, 5, 15));
//solicta al objeto database los primeros n registros
dataBase.Siguientes ();
System.Data.DataView registrosTemp;
dataViewTemp = dataBase.ExecuteAdapter(mostrando.IdUltimoRegistro);
if(dataView.Count!=0) {
//Guarda el valor del primer y del ultimo registro
mostrando.PrimerRegistro=((int)dataView[0][0]);
mostrando.UltimoRegistro =((int)dataView[dataView.Count-1][0]);
registries=registriesTemp;
}
//Es la primera pagina.
numeroDePagina.Primera ();
userInterfaceBrowser.Display(registros, numeroDePagina);
} else {
//Extrae el nombre del evento
System.Collections.Specialized.NameValueCollection coll;
coll=Request.Form;
string evento;
evento=coll["__EVENTARGUMENT"];
//realiza el caso de uso siguiente
//muestra los siguientes n registros
if(event=="Siguiente") {
dataBase.Siguientes ();
System.Data.DataView registrosTemp;
//Solicita al objeto base de datos los siguientes n regsitros a partir del ultimo registro que se estaba mostrando
registrosTemp = dataBase.EjecutarAdapter(mostrando.UltimoRegistro);
if(registries.Count!=0) {
registros=registrosTemp;
// Si la base de datos retorna algún registro, se guarda el valor del primer y
// ultimo registro y se incrementa el numero de pagina
mostrando.PrimerRegistro=((int)dataView[0][0]);
mostrando.UltimoRegistro =((int)dataView[registers.Count-1][0]);
numeroDePagina.Siguiente ();
}
userInterfaceBrowser.Display(registros, numeroDePagina);
}
//mostrar los registros anteriores
if(event=="Anteriores") {
// similar a siguente
}
// realiza el caso de uso borrar
if(event=="Borrar") {
int idRegistro;
idRegistro= userInterfaceBrowser.Borrar ();
string resultadoDb;
// Busca los registros seleccinados para borrar
// si hay alguno retorna la clave, sino return -1
// Temina el ciclo cuando no haya ningun registro para borrar while(idRegistro!=-1) {
// solicita al objeto base de datos que borre los registros.
dataBase.Borrar(idRegistro);
resultadoDb=dataBase.ExecuteBorrar ();
idRegistro=userInterfaceBrowser.Borrar ();
}
dataBase.Close();
// muestra los registro luego de borrar los seleccionados
dataBase=new DataBaseWrapper.DataBase();
dataBase.Actuales();
System.Data.DataView registrosTemp;
registroTemp = dataBase.EjecutarAdapter(mostrando.PrimerRegistro);
if(registriesTemp.Count!=0 ) {
registros=registrosTemp;
mostrando.PrimerRegistro=((int)dataView[0][0]);
mostrando.UltimoRegistro =((int)dataView[registers.Count-1][0]);
numeroDePagina.Siguiente ();
}
//envia los registros encontrados y el numero de pagina al browser para ser
// mostrados
userInterfaceBrowser.Mostrar (registros, numeroDePagina);
}
}
dataBase.Close();
}
string scriptStr;
scriptStr="<script language=\"JavaScript\">\n";
scriptStr+=
"function ToSendToTheServantTheEvent_Delete() {\n"+
Page.GetPostBackEventReference(this.Page,"Delete")+"\n"+
"}\n";
scriptStr+=
"function ToSendToTheServantTheEvent_Siguientes() {\n"+
Page.GetPostBackEventReference(this.Page,"Siguientes")+"\n"+
"}\n";
scriptStr+=
"function ToSendToTheServantTheEvent_Anteriores () {\n"+
Page.GetPostBackEventReference (this.Page,"Anteriores")+"\n"+
"}\n";
scriptStr+=
"function ToSendToTheServantTheEvent_Borrar () {\n"+
Page.GetPostBackEventReference(this.Page,"Borrar")+"\n"+
"}\n";
scriptStr+=
"</"+"script>";
if(!this.IsClientScriptBlockRegistered("scriptStr"))
this.RegisterClientScriptBlock("scriptStr", scriptStr);
}
}
}
B – DataBase Wrapper Registros
namespace DataBaseWrapper {
public class Registros {
private System.Data.SqlClient.SqlConnection myConn;
private System.Data.SqlClient.SqlCommand myCommandCliente;
public Registros () {
/*
CREATE TABLE BuzonDeEmail (
idRegistro int NOT NULL IDENTITY PRIMARY KEY,
asunto varchar(100) NOT NULL DEFAULT (''),
fecha smalldatetime NOT NULL DEFAULT (0)
)
*/
System.String StringDeConeccion;
this.myConn = new System.Data.SqlClient.SqlConnection();
StringDeConeccion=System.Configuration.ConfigurationSettings.AppSettings["StringDeConeccion"];
this.myConn.ConnectionString = StringDeConeccion;
if(this.myConn.State != System.Data.ConnectionState.Open)
this.myConn.Open();
}
public void Siguientes () {
/*
CREATE PROCEDURE Siguientes
@fecha char(40)
AS
SELECT TOP 8 "+
fecha, "+
asunto, "+
idRegistro
FROM BuzonDeEmail "+
WHERE "+
(fecha < @fecha)"+
ORDER BY fecha DESC";
*/
string strComm;
strComm = "Siguientes";
this.myCommandCliente = new System.Data.SqlClient.SqlCommand();
this.myCommandCliente.Connection = this.myConn;
this.myCommandCliente.CommandText=strComm ;
this.myCommandCliente.CommandType=System.Data.CommandType.StoredProcedure ;
}
public void Anteriores () {
// similar a anteriores
}
public System.Data.DataView ExecuteAdapter(string fechaRegistro) {
System.Data.SqlClient.SqlDataAdapter dataAdapter;
System.Data.IDataParameterCollection parameters;
System.Data.SqlClient.SqlParameter fechaRegistroParameter;
System.Data.DataSet dataSet;
System.Data.DataTable dataTable;
System.Data.DataView registros;
dataAdapter=new System.Data.SqlClient.SqlDataAdapter();
dataAdapter.SelectCommand=this.myCommandCliente ;
parametros=dataAdapter.SelectCommand.Parameters;
System.Data.SqlClient.SqlParameter fechaParametros;
fechaRegistroParametros=
new System.Data.SqlClient.SqlParameter("@fecha", System.Data.SqlDbType.SmallDateTime );
fechaRegistroParametros.Value=System.Convert.ToDateTime (fechaRegistro);
parametrers.Add(fechaRegistroParameter);
dataSet = new System.Data.DataSet();
dataAdapter.MissingSchemaAction = System.Data.MissingSchemaAction.AddWithKey ;
dataAdapter.Fill(dataSet);
dataTable=dataSet.Tables[0];
registros = dataTable.DefaultView ;
registros.Sort = "fecha desc";
dataAdapter.Dispose();
dataAdapter=null;
dataSet.Dispose ();
dataSet=null;
return registros;
}
public void Borrar (int idRegistro) {
/*
CREATE PROCEDURE Borrar
@idRegistro int
AS
DELETE FROM BuzonDeEmail
WHERE idRegistro =@idRegistro
*/
System.Data.IDataParameterCollection parameters;
System.Data.SqlClient.SqlParameter rregistriesIdSqlParameter;
this.myCommandCliente = new System.Data.SqlClient.SqlCommand ();
this.myCommandCliente.Connection = this.myConn;
this.myCommandCliente.CommandType=System.Data.CommandType.Text;
this.myCommandCliente.CommandText=
"Borrar";
this.myCommandCliente.CommandType=System.Data.CommandType.StoredProcedure ;
parameters=this.myCommandCliente.Parameters;
registerKeySqlParameter=
new System.Data.SqlClient.SqlParameter("@registriesId",System.Data.SqlDbType.Int );
parametros.Add(registerKeySqlParameter);
this.myCommandCliente.Parameters["@registriesId"].Value=
registriesId;
}
public string ExecuteDelete() {
string resultDb;
resultDb="ok";
try {
this.myCommandCliente.ExecuteNonQuery ();
resultDb= "ok";
} catch(System.Exception exception) {
resultDb=exception.ToString();
}
return resultDb;
}
public void Close() {
if(this.myConn!=null )
this.myConn.Close();
}
}
}
C - Data Abstraction Estado
namespace DataAbstraction {
public class Estado {
private System.Web.UI.Page myPage;
private System.Web.UI.Control myForm;
private System.Web.UI.HtmlControls.HtmlGenericControl dataAbstractionState;
private System.Web.UI.HtmlControls.HtmlInputHidden registriesFirst;
public Estado(System.Web.UI.Page myPage) {
this.myPage=myPage;
myForm=myPage.FindControl("myForm");
dataAbstractionState=
(System.Web.UI.HtmlControls.HtmlGenericControl )myForm.FindControl("dataAbstractionState");
registriesFirst=
(System.Web.UI.HtmlControls.HtmlInputHidden) dataAbstractionState.FindControl("registriesFirst");
}
public string RegistriesFirst() {
set {
registriesFirst.Value=value;
}
get {
string result;
string registriesFirstStr;
registriesFirstStr=registriesFirst.Value;
if(registriesFirstStr!=null)
result=registriesFirstStr;
else
result="";
return result;
}
}
}
D – User Interface. Browser
namespace userInterface {
public class Browser {
private System.Web.UI.Page myPage;
private int t;
public Browser(System.Web.UI.Page myPage) {
this.myPage=myPage;
this.t=0;
}
// It returns the value of the key of the next registration selected to erase, if there is some, but it returns -1
public int Delete() {
System.Web.UI.Control myForm;
System.Web.UI.HtmlControls.HtmlGenericControl userInterfaceBrowser;
System.Web.UI.Control registersRepeater;
System.Web.UI.ControlCollection registersRepeaterCollection;
System.Web.UI.Control registersRepeaterItem;
System.Web.UI.ControlCollection registersRepeaterItemCollection;
System.Web.UI.Control htmlInput;
string registriesIdStr;
int registriesId;
bool selected;
myForm=myPage.FindControl("myForm");
userInterfaceBrowser=
(System.Web.UI.HtmlControls.HtmlGenericControl)
myForm.FindControl("userInterfaceBrowser");
registersRepeater=userInterfaceBrowser.FindControl ("registers");
registersRepeaterCollection = registersRepeater.Controls;
registersRepeaterItem= registersRepeaterCollection.FindControl("myRepeaterItem");
registersRepeaterItemCollection =registersRepeaterItem.Controls ;
//it initializes the cycle not selecting any registration
registriesIdStr="-1";
registriesId=-1;
selected=false;
// I leave the cycle for anyone of the following 2 reasons
while(t<registersRepeaterItemCollection.Count && !seleccionado) {
htmlInput=registersRepeaterItemCollection[t];
if(htmlInput is System.Web.UI.HtmlControls.HtmlInputCheckBox) {
//found a selected registration
selected=
((System.Web.UI.HtmlControls.HtmlInputCheckBox)htmlInput).Checked;
registriesIdStr=
((System.Web.UI.HtmlControls.HtmlInputCheckBox )htmlInput).Value;
registriesId=System.Convert.ToInt16(registriesIdStr);
}
//you fixes in all the registration shown in it paginates it
// to notice the next registration
this.t=this.t+1;
} }
//notices if this selected to erase
if(selected) {
return registriesId;
} else {
return -1; }
}
public void Display(System.Data.DataView registers, int numberOfPaginates) {
System.Web.UI.Control myForm;
myForm=myPage.FindControl("myForm");
System.Web.UI.WebControls.Repeater registersRepeater;
registersRepeater=(System.Web.UI.WebControls.Repeater)myForm.FindControl("registers");
registersRepeater.DataSource=registers;
registersRepeater.DataBind();
System.Web.UI.HtmlControls.HtmlGenericControl numberOfPaginates;
numberOfPaginates=
(System.Web.UI.HtmlControls.HtmlGenericControl) myForm.FindControl("numberOfPaginates");
numberOfPaginates.InnerHtml=System.Convert.ToString (numberOfPaginates);
}
}
}
- Fundaments of Software Engineering. Carlo Gehezzi, Mehdi Jazayevi, Dino Mandrioli
- Designing Concurrent, Distributed, and Real-Time Applications with Uml. Hassan Gomaa
- . NET Application Development with C#, ASP.NET, ADO.NET and Web Services. Hanspoter Mossenbock, Wolfgang Beer, Dietrich Bringrubel and Albrecht Wob
marcosgabrielcravero@gmail.com
Developer of applications for Internet freelance
Copyright. Cravero Gabriel. 2006
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free documentation License, Version 1.2 or
any later version published by the Free Software Foundation. A copy of
the license is included in the section entitled "GNU Free
Documentation License
Si quieres un presupuesto o contratarme para un trabajo freelance, part-time o full-time escribime y a la brevedad te respondere.
Tambien puedes escribirme a marcosgabrielcravero@gmail.com.