31 de MAYO de 2001

Transcripción

31 de MAYO de 2001
INGENIERÍA DEL SOFTWARE. 4º ING. INFORMÁTICA (UPV/EHU)
31 de MAYO de 2001
NOMBRE:
GRUPO:
1.-¿Es posible que un sistema que no ofrezca mecanismos de HERENCIA presente la
característica conocida por POLIMORFISMO? Razona la respuesta. (0,5 ptos.)
No es posible. POLIMORFISMO es una característica OO que permite que variables del tipo (clase) X
puedan contener instancias de subclases de X. No se pueden definir subclases si no hay HERENCIA
NOTA: una respuesta contraria justificándola como que se puede tener POLIMORFISMO en un
sistema que sólo ofrezca mecanismos de interfaces como el de Java será considerada válida.
2.-¿Qué es EXTENSIBILIDAD y REUTILIZACIÓN del software? ¿Qué características propias
de los sistemas OO favorecen la EXTENSIBILIDAD y la REUTILIZACIÓN y por qué? (1 pto.)
EXTENSIBILIDAD es un factor de calidad del software que consiste en la facilidad de adaptación del
software a nuevos requisitos o cambios en la especificación. REUTILIZACIÓN es otro factor de
calidad que consiste en crear elementos de software que sirvan para construir distintas aplicaciones. El
POLIMORFISMO, la LIGADURA DINÁMICA (así como el mecanismo de INTERFACES que se
basa en ellos) favorecen la EXTENSIBILIDAD y REUTILIZACIÓN. Una clase que computa algo útil
con objetos de una clase P (o interfaz I) PUEDE REUTILIZARSE para objetos de cualquier subclase
de P (o clase que implemente I). Se PUEDE EXTENDER (y cambiar) el comportamiento de una clase
definiendo una subclase que añada y/o redefina métodos existentes. La LIGADURA DINÁMICA
asegurará que sean estos últimos los que se ejecuten.
3.- En el Proceso Unificado de Desarrollo de software, ¿se considera que la CAPTURA DE
REQUISITOS, ANÁLISIS, DISEÑO, IMPLEMENTACIÓN y PRUEBAS constituyen las
FASES del proyecto? (0,5 ptos.)
No. Son los FLUJOS DE TRABAJO (el conjunto de actividades) a realizar a lo largo del Ciclo de
Vida del proyecto (que, por cierto, es el que se divide en FASES que terminan con ciertos HITOS, y
que a su vez están formadas por ITERACIONES)
4.- Explica qué son los RESGUARDOS, en qué “PARTE” del ciclo de vida de un proyecto tienen
importancia y cuál es su función. (0,5 ptos.)
Un RESGUARDO es un programa/módulo que sustituye a otro y que se comporta como si se hubiera
llamado al módulo real (aunque existen distintas posibilidades). Los RESGUARDOS se utilizan en el
flujo de trabajo de PRUEBAS. Cuando se prueba un módulo que llama a otros módulos y no se sabe si
estos últimos son correctos (en INTEGRACIÓN DESCENDENTE) se sustituyen por RESGUARDOS
y así, si los resultados no son los esperados, entonces se sabe que el módulo erróneo es el que llama al
RESGUARDO.
5.- Programar el siguiente método (que es uno de los algoritmos reutilizables de JGL) (1,25 pto.)
/**
* @param container Un contenedor.
* @param predicate Un predicado unario.
* @return Nuevo contenedor que tiene los elementos que satisfacen el predicado
*/
public static Container select( Container container, UnaryPredicate predicate );
perteneciente a la clase public final class Filtering
public static Container select(Container container, UnaryPredicate predicate) {
Container nuevo = container.clone();
Object o;
nuevo.clear();
Enumeration e = container.elements();
while (e.hasMoreElements())
{
o=e.nextElement();
if (predicate.execute(o)) nuevo.add(o); }
return nuevo; }
6.- Utilizando el algoritmo Filtering.select de JGL, se pide completar el siguiente código
para que, dado un Array JGL que contiene varias personas (objetos de la clase Pers definida en
el ANEXO), se obtenga otro Array JGL con solamente las personas pertenecientes al primer
Array que sean rubias. (1,25 pto.)Array contenedor = new Array( );
contenedor.add(new Pers("pepe",32,"RUBIO"));
contenedor.add(new Pers("ana",33,"RUBIO"));
contenedor.add(new Pers("pepe",30,"CASTAÑO"));
.....
// COMPLETA ESTE CÓDIGO USANDO Filtering.select(............);
Array res = Filtering.select(contenedor, new SeleccionarRubios());
Donde se define la clase:
public class SeleccionarRubios implements UnaryPredicate {
public boolean execute (Object o) {
return (Pers)o.esRubio();}}
7.- La interfaz de usuario asociada a un caso de uso llamado CONSULTAR PRECIO aparece a
continuación, junto con la clase Java correspondiente: (2,5 ptos.)
import java.awt.*;
import java.awt.event.*;
public class ConsPrecioIU extends Frame {
Label label1 = new Label();
Panel panel1 = new Panel();
Button button1 = new Button();
Button button2 = new Button();
Panel panel2 = new Panel();
GridLayout gridLayout1 = new GridLayout(3,2);
Label label2 = new Label();
TextField textField1 = new TextField();
Label label3 = new Label();
TextField textField2 = new TextField();
Label label4 = new Label();
TextField textField3 = new TextField();
public ConsPrecioIU() {
super();
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setTitle("Frame Title");
label1.setText("CONSULTAR PRECIO");
label1.setAlignment(Label.CENTER);
button1.setLabel("Consultar Precio");
button1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button1_actionPerformed(e); } });
button2.setLabel("Cancelar");
button2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button2_actionPerformed(e); } });
label2.setText("MANZANAS (Kg.)");
label3.setText("PERAS (Kg.)");
label4.setText("NARANJAS (Kg.)");
panel2.setLayout(gridLayout1);
this.add(label1, BorderLayout.NORTH);
this.add(panel1, BorderLayout.SOUTH);
panel1.add(button1, null);
panel1.add(button2, null);
this.add(panel2, BorderLayout.CENTER);
panel2.add(label2, null);
panel2.add(textField1, null);
panel2.add(label3, null);
panel2.add(textField2, null);
panel2.add(label4, null);
panel2.add(textField3, null);
this.pack();
this.setVisible(true);
}
void button1_actionPerformed(ActionEvent e) {
}
void button2_actionPerformed(ActionEvent e) {
}
}
Se dispone también de una clase llamada Aviso que sirve para crear Dialog modales asociados al
objeto Frame actual. La llamada new Aviso(this,"Pulsa Aceptar y me voy");
crearía lo siguiente:
Además, nos han proporcionado los siguientes métodos, los cuales no sabemos ni a qué clase
pertenecen ni qué es lo que hacen exactamente, pero nos han dicho que son útiles para acceder a
los datos almacenados en la siguiente tabla de una BD Access. Además nos dicen que dicha BD es
accesible por medio de una fuente de datos ODBC llamada PRODS
public void inicializarBD () {
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
conexion=DriverManager.getConnection("jdbc:odbc:PRODS");
sentencia=conexion.createStatement(); }
catch(Exception e){System.out.println("Error"+e.toString());} }
public float getPrecio(String nombre) {
try{ rs=sentencia.executeQuery("SELECT PRECIO FROM PRODUCTOS "+
"WHERE NOMBRE='"+nombre+"'");
if (rs.next()) return rs.getFloat("PRECIO");
} catch (Exception e) {System.out.println("Error: "+e.toString());}
return 0; }
Se pide: Rellenar la clase ConsPrecioIU con el código necesario para que al pulsar el botón
CONSULTAR PRECIO aparezca como resultado el precio de los productos escogidos.
Por ejemplo, el resultado sería el siguiente:
si los precios actuales fueran los que aparecen en la tabla ACCESS anterior y se hubiera pulsado el botón
CONSULTAR PRECIO con los siguientes datos de entrada:
NOTA1: Sólo se admitirá una solución que
•
A) Utilice una arquitectura lógica en 3 niveles. Indicar cuáles son esos niveles y qué partes de
código y/o clases pertenecen a cada nivel.
•
B) Sea extensible ante el siguiente cambio en la lógica del negocio. “Se van a aplicar
porcentajes de descuento a cada producto dependiendo de la cantidad de Kg. que se compre”.
Indicar por qué la solución propuesta es extensible ante ese cambio.
NOTA2: Indicar CLARAMENTE dónde se debe añadir el código en la clase ConsPrecioIU y
dónde hay que declarar los métodos inicializarBD () y getPrecio(String
nombre)
SOLUCIÓN:
El cuerpo del procedimiento void button1_actionPerformed(ActionEvent e) {
} de la clase ConsPrecioIU podría ser:
Precios p = new Precios(); // Creación de un objeto lógica del negocio
float precio = p.calcularPrecio(textField1.getText(),
textField2.getText(),
textField3.getText());
Aviso a = new Aviso(this,"Precio es: "+precio);
Existirá una clase con la lógica del negocio Precios que además podrá contener los métodos que
llaman al nivel de datos: inicializarBD () y getPrecio(String nombre) y el
siguiente:
public float calcularPrecio(String kgManz, String kgPer, String kgNar) {
float m,p,n;
try{m=Float.parseFloat(kgManz);} catch (Exception ex) {m=0;}
try{p=Float.parseFloat(kgPer);} catch (Exception ex) {p=0;}
try{n=Float.parseFloat(kgNar);} catch (Exception ex) {n=0;}
return m*getPrecio("MANZANAS (Kg.)")+p*getPrecio("PERAS (Kg.)")
+ n*getPrecio("NARANJAS (Kg.)");}
NIVEL DE PRESENTACIÓN: ConsPrecioIU y Aviso
NIVEL DE LÓGICA DEL NEGOCIO: Precios (que incluye el código JDBC)
NIVEL DE DATOS: La base de datos Access
Justificación de por qué la solución es extensible: En el momento en el que haya que extender la
aplicación para tratar el nuevo requisito, NO AFECTARÁ AL NIVEL DE PRESENTACIÓN. Sólo se
verán afectados el NIVEL DE DATOS (habrá que añadir tablas que permitan almacenar los
descuentos para cada producto) y el NIVEL LÓGICA DEL NEGOCIO (que tendrá que calcular los
nuevos precios aplicando los descuentos).
NOTA: no sería necesario ni recompilar el nivel de presentación (la clase ConsPrecioIU). De
hecho, se podría cambiar la lógica del negocio (proporcionar una nueva clase Precios) en tiempo
de ejecución y no haría falta ni volver a crear el objeto gráfico (instancia de ConsPrecioIU) !!
Esto último es cierto porque la instancia del objeto con la lógica del negocio se crea en el método de
respuesta al evento del botón.
La solución proporcionada es válida pero puede ser mejorada en algunos aspectos: (esto no se pedía)
1.- Los métodos inicializarBD () y getPrecio(String nombre) se pueden poner en
otra clase aparte (por ejemplo AccesoBD), la cual puede ser Singleton y que controle que sólo haya
una conexión abierta (llamando a inicializarBD () en el constructor de la clase)
2.- Se puede no crear el objeto de negocio (instancia de Precios) dentro del método de respuesta al
evento sobre el botón sino en el constructor de ConsPrecioIU para que sólo se haga una vez.
También se podría pasar como parámetro en el método constructor un objeto con la lógica del negocio
a aplicar.
3.- Se pueden ahorrar llamadas a getPrecio(String nombre) en el método
calcularPrecio(...) de Precios, si se calculan y almacenan los valores de
getPrecio("MANZANAS (Kg.)"), getPrecio("PERAS”),...(por ejemplo en el constructor
de Precios).Esto sería válido sólo si los precios no cambiaran a lo largo de la vida del objeto de
negocio (objeto de Precios).
8.- Existe otro caso de uso que sirve para consultar la disponibilidad de los productos (1,5 ptos)
Describe cómo se puede definir una clase REUTILIZABLE que sirva para generar la Interfaz de
Usuario del caso de uso CONSULTAR PRECIOS, del caso de uso CONSULTAR
DISPONIBILIDAD y de cualquier otro caso de uso que pueda definirse en el futuro y que
necesite una interfaz de usuario similar. NOTA: aseguraros de que permita ejecutar distintas
acciones al pulsar el botón, dependiendo de qué caso de uso se trate.
SOLUCIÓN:
Se puede aplicar el patrón de diseño INTERFAZ
Clase Consultar
Consultar(IntConsultar i)
usa
interfaz IntConsultar
getTitulo(): String
getTituloBoton(): String
ejecutarBoton(String s1,s2,s3): void
La clase reutilizable Consultar sería como la clase anterior ConsPrecioIU con los siguientes cambios: (en
negrita)
import ...
public class Consultar extends Frame {
Label label1 = new Label();
...
TextField textField3 = new TextField();
IntConsultar interfaz;
public Consultar (IntConsultar i) {
super();
try {
interfaz=i;
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setTitle("Frame Title");
label1.setText(interfaz.getTitul o());
label1.setAlignment(Label.CENTER);
button1.setLabel interfaz.getTituloBoton());
button1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button1_actionPerformed(e);
}
});
....
label2.setText("MANZANAS (Kg.)");
label3.setText("PERAS (Kg.)");
label4.setText("NARANJAS (Kg.)");
.....
void button1_actionPerformed(ActionEvent e) {
interfaz.ejecutarBoton(textField1.getText(),
textField2.getText(),
textField3.getText());
}
Donde la definición de la interfaz es la siguiente:
public interface IntConsultar {
public String getTitulo();
public String getTituloBoton();
public void ejecutarBoton(String s1,String s2,String s3);
}
Con la clase reutilizable Consultar se puede ver cómo se podrían crear objetos de la clase
ConsPrecioIU del ejercicio anterior (la cual ya no sería necesaria):
Consultar c = new Consultar(new ConsultarPrecios());
Donde ConsultarPrecios sería:
public class ConsultarPrecios implements IntConsultar {
public String getTitulo() { return “CONSULTAR PRECIO”;}
public String getTituloBoton() {return “Consultar Precio”;}
public void ejecutarBoton(String s1, String s2, String s3) {
Precios p = new Precios();
float precio = p.calcularPrecio(s1,s2,s3);
Aviso a = new Aviso(this,"Precio es: "+precio);}
9.- Describe también cómo se puede conseguir que la clase que genera el interfaz gráfico sea
REUTILIZABLE para cualquier producto, esto es, permita CONSULTAR PRECIOS,
DISPONIBILIDADES, etc. de un conjunto de N productos cualesquiera y no sirva sólo para
MANZANAS, PERAS y NARANJAS (1 pto)
NOTA: no es necesario programarlo, sino indicar qué clases, métodos, etc. se necesitarían.
SOLUCIÓN:
Se puede aplicar el patrón de diseño INTERFAZ
Clase Consultar
interfaz IntConsultar
usa
Consultar(IntConsultar i)
getTitulo(): String
getTituloBoton(): String
ejecutarBoton(Enumeration e): void
getProductos(): Enumeration
Se deja como ejercicio el programarlo (no era necesario hacerlo en el examen)
ANEXO
public interface Container extends Cloneable, Serializable
{
/**
* Return a shallow copy of myself.
*/
public Object clone();
/**
* Return a string that describes me.
*/
public String toString();
/**
* Return true if I'm equal to a specified object.
* @param object The object to compare myself against.
* @return true if I'm equal to the specified object.
*/
public boolean equals( Object object );
/**
* Return the number of objects that I contain.
*/
public int size();
/**
* Return the maximum number of objects that I can contain.
*/
public int maxSize();
/**
* Return true if I contain no objects.
*/
public boolean isEmpty();
/**
* Remove all of my objects.
*/
public void clear();
/**
* Return an Enumeration of the components in this container
*/
public Enumeration elements();
/**
* Return an iterator positioned at my first item.
*/
public ForwardIterator start();
/**
* Return an iterator positioned immediately after my last item.
*/
public ForwardIterator finish();
/**
* Add an object to myself. If appropriate, return the object that
* displaced it, otherwise return null.
*/
public Object add( Object object );
/**
* Remove the element at a particular position.
*/
public Object remove( Enumeration pos );
/**
* Remove the elements in the specified range.
*/
public int remove( Enumeration first, Enumeration last );
}
public interface BinaryPredicate extends Serializable
{
/**
* Return the result of executing with two Object arguments.
* @param first The first object operand.
* @param second The second object operand.
* @return The boolean result of processing the input parameters.
*/
boolean execute( Object first, Object second );
}
public class Pers {
private String nombre;
private int dni;
private String colorPelo;
Pers(String n, int d, String c)
{ nombre=n; dni=d; colorPelo=c;}
public int getDni() {return dni;}
public String getNombre() {return nombre;}
public String getColorPelo() {return colorPelo;}
public String esRubio() {return colorPelo.equals(“RUBIO”);}
public String toString() {
return nombre + "/" + dni; }
}

Documentos relacionados