viernes, 18 de noviembre de 2011

Patron Builder




El patrón Builder o Constructor es otro de los patrones creacionales. Estepatrón permite separar la construcción de un objeto complejo de su representación, de modo que el mismo proceso de construcción pueda crear diferentes representaciones de este objeto.

La siguiente imagen muestra el diagrama de clases de este patrón:



  • Builder: interfaz abstracta para crear productos.
  • Concrete Builder: implementación del Builder, construye y reúne las partes necesarias para construir los productos.
  • Director: construye un objeto usando el patrón Builder.
  • Producto: El objeto complejo bajo construcción.

A continuación se muestra un ejemplo de este patrón, extraído de la Wikipedia. En este ejemplo tenemos un cocina (Director) que produce pizzas (Producto) de con diferentes características (Concrete Builder):


/** "Product" */
class Pizza {
 private String dough = "";
 private String sauce = "";
 private String topping = "";
 
 public void setDough(String dough) {
  this.dough = dough;
 }
 
 public void setSauce(String sauce) {
  this.sauce = sauce;
 }
 
 public void setTopping(String topping) {
  this.topping = topping;
 }
}
 
/** "Abstract Builder" */
abstract class PizzaBuilder {
 protected Pizza pizza;
 
 public Pizza getPizza() {
  return pizza;
 }
 
 public void createNewPizzaProduct() {
  pizza = new Pizza();
 }
 
 public abstract void buildDough();
 
 public abstract void buildSauce();
 
 public abstract void buildTopping();
}

/** "ConcreteBuilder" */
class HawaiianPizzaBuilder extends PizzaBuilder {
 public void buildDough() {
  pizza.setDough("cross");
 }
 
 public void buildSauce() {
  pizza.setSauce("mild");
 }
 
 public void buildTopping() {
  pizza.setTopping("ham+pineapple");
 }
}
 
/** "ConcreteBuilder" */
class SpicyPizzaBuilder extends PizzaBuilder {
 public void buildDough() {
  pizza.setDough("pan baked");
 }
 
 public void buildSauce() {
  pizza.setSauce("hot");
 }
 
 public void buildTopping() {
  pizza.setTopping("pepperoni+salami");
 }
}

/** "Director" */
class Cook {
 private PizzaBuilder pizzaBuilder;
  
 public void setPizzaBuilder(PizzaBuilder pb) {
  pizzaBuilder = pb;
 }
 
 public Pizza getPizza() {
  return pizzaBuilder.getPizza();
 }
 
 public void constructPizza() {
  pizzaBuilder.createNewPizzaProduct();
  pizzaBuilder.buildDough();
  pizzaBuilder.buildSauce();
  pizzaBuilder.buildTopping();
 }
}
 
/** A given type of pizza being constructed. */
public class BuilderExample {
 public static void main(String[] args) {
  Cook cook = new Cook();
  
  PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
  PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
  
  cook.setPizzaBuilder(hawaiianPizzaBuilder);
  cook.constructPizza();
 
  Pizza pizza = cook.getPizza();
 }
}

Hay quién confunde el patrón Builder con Factory, pero la diferencia es que, mientras el patrón Factory difiere la elección de la clase concreta de un objeto, el patrón Builder simplemente oculta la lógica de creación de un objeto complejo. 

Video Explicativo







jueves, 17 de noviembre de 2011

Patrones de Diseño

  • Los patrones de diseño abstraen en el proceso de instanciación.
  • Ayudan a hacer un sistema independiente de cómo los objetos son creados, compuestos y representados.
  • Introducen una gran dosis de flexibilidad en todos los aspectos que involucren creación de objetos.

Características:
  • Encapsulan el conocimiento sobre las subclases concretas que el sistema utiliza.
  • Ocultan como las instancias de las clases son creadas y puestas juntas.
Cada vez que se comienza el desarrollo de una nueva aplicación, como ingenieros de software nos volvemos a enfrentar a los mismos o similares problemas que ya se han encarado en anteriores desarrollos. No sólo esto, nos estamos enfrentando en muchos casos a los mismo problemas que otros ya han tenido que resolver.

Aprovechar todo el conocimiento anterior es útil y permite ganar tiempo en el diseño y desarrollo de nuevas aplicaciones. Esto es algo de sobra conocido y por ello han surgido los patrones de diseño, como pautas a seguir en determinadas condiciones avaladas por el conocimiento previo.

En la construcción, el arquitecto Christopher Alexander propuso el aprendizaje y uso de una serie de patrones para la construcción de edificios de una mayor calidad. Según sus palabras, "cada patrón describe un problema que ocurre infinidad de veces en nuestro entorno, así como la solución al mismo, de tal modo que podemos utilizar esta solución un millón de veces más adelante sin tener que volver a pensarla otra vez."

Los patrones que Christopher Alexander y sus colegas definieron son un intento de formalizar y plasmar de una forma práctica generaciones de conocimiento arquitectónico. Los patrones no son principios abstractos que requieran su redescubrimiento para obtener una aplicación satisfactoria, ni son específicos a una situación particular o cultural; son algo intermedio. Un patrón define una posible solución correcta para un problema de diseño dentro de un contexto dado, describiendo las cualidades invariantes de todas las soluciones.

Llevando la idea de patrón al mundo de la informática, un patrón de diseño es una solución a un problema de diseño que debe poseer las siguientes características:

  • Debe haber comprobado su efectividad resolviendo problemas similares en ocasiones anteriores.
  • Debe ser reusable, lo que significa que es aplicable a diferentes problemas de diseño en distintas circunstancias.

Los patrones de diseño tuvieron un gran éxito a partir de la publicación del libroDesign Patterns escrito por el grupo Gang of Four (GoF, en el que se recogían 23 patrones de diseño comunes. Estos patrones definidos por el GoF están divididos en 3 grupos, patrones creacionales, estructurales y de comportamiento.

Patrones creacionales: Tratan de solucionar los problemas que pueden surgir en la creación de objetos.
  • Abstract Factory: Proporciona una interfaz para crear familias de objetos o que dependen entre sí, sin especificar sus clases concretas.
  • Factory Method: Define una interfaz para crear un objeto, pero deja que sean las subclases quienes decidan qué clase instanciar. Permite que una clase delegue en sus subclases la creación de objetos.
  • Builder: Separa la construcción de un objeto complejo de su representación, de forma que el mismo proceso de construcción pueda crear diferentes representaciones.
  • Prototype: Especifica los tipos de objetos a crear por medio de una instancia prototípica, y crear nuevos objetos copiando este prototipo.
  • Singleton: Garantiza que una clase sólo tenga una instancia, y proporciona un punto de acceso global a ella.

Patrones estructurales: Tratan de facilitar el diseño identificando formas simples de establecer relaciones entre entidades.
  • Adapter: Convierte la interfaz de una clase en otra distinta que es la que esperan los clientes. Permiten que cooperen clases que de otra manera no podrían por tener interfaces incompatibles.
  • Bridge: Desvincula una abstracción de su implementación, de manera que ambas puedan variar de forma independiente.
  • Composite: Combina objetos en estructuras de árbol para representar jerarquías de parte-todo. Permite que los clientes traten de manera uniforme a los objetos individuales y a los compuestos.
  • Decorator: Añade dinámicamente nuevas responsabilidades a un objeto, proporcionando una alternativa flexible a la herencia para extender la funcionalidad.
  • Facade: Proporciona una interfaz unificada para un conjunto de interfaces de un subsistema. Define una interfaz de alto nivel que hace que el subsistema se más fácil de usar.
  • Flyweight: Usa el compartimiento para permitir un gran número de objetos de grano fino de forma eficiente.
  • Proxy: Proporciona un sustituto o representante de otro objeto para controlar el acceso a éste.

Patrones de comportamiento: Identifican patrones comunes de comunicación entre objetos y tratan de incrementar la flexibilidad de esta comunicación.
  • Chain of Responsibility: Evita acoplar el emisor de una petición a su receptor, al dar a más de un objeto la posibilidad de responder a la petición. Crea una cadena con los objetos receptores y pasa la petición a través de la cadena hasta que esta sea tratada por algún objeto.
  • Command: Encapsula una petición en un objeto, permitiendo así parametrizar a los clientes con distintas peticiones, encolar o llevar un registro de las peticiones y poder deshacer la operaciones.
  • Interpreter: Dado un lenguaje, define una representación de su gramática junto con un intérprete que usa dicha representación para interpretar las sentencias del lenguaje.
  • Iterator: Proporciona un modo de acceder secuencialmente a los elementos de un objeto agregado sin exponer su representación interna.
  • Mediator: Define un objeto que encapsula cómo interactúan un conjunto de objetos. Promueve un bajo acoplamiento al evitar que los objetos se refieran unos a otros explícitamente, y permite variar la interacción entre ellos de forma independiente.
  • Memento: Representa y externaliza el estado interno de un objeto sin violar la encapsulación, de forma que éste puede volver a dicho estado más tarde.
  • Observer: Define una dependencia de uno-a-muchos entre objetos, de forma que cuando un objeto cambia de estado se notifica y actualizan automáticamente todos los objetos.
  • State: Permite que un objeto modifique su comportamiento cada vez que cambia su estado interno. Parecerá que cambia la clase del objeto.
  • Strategy: Define una familia de algoritmos, encapsula uno de ellos y los hace intercambiables. Permite que un algoritmo varíe independientemente de los clientes que lo usan.
  • Template Method: Define en una operación el esqueleto de un algoritmo, delegando en las subclases algunos de sus pasos. Permite que las subclases redefinan ciertos pasos del algoritmo sin cambiar su estructura.
  • Visitor: Representa una operación sobre los elementos de una estructura de objetos. Permite definir una nueva operación sin cambiar las clases de los elementos sobre los que opera.

Con el tiempo, han ido surgiendo nuevos patrones, sobre todo dentro del ámbito deldesarrollo Java empresarial. Algunos de ellos son:
  • Controlador frontal
  • Modelo Vista Controlador (MVC)
  • Vista de composición
  • Fachada de sesión - Sesion Facade
  • Localizador de servicio - Service Locator
  • Objeto valor - Value object
  • Objeto de Transferencia de Datos - Data Transfer Object (DTO)
  • Delegado de empresa - Bussiness Delegate
  • Objeto de acceso a datos - Data Access Object (DAO)
  • Dependency Injection(DI) - Inversion of Control (IoC)
  • Open Session In View (OSIV)
  • ThreadLocal Session

En este artículo sólo he tratado de dar una introducción a los patrones de diseño. En posteriores artículos trataré de desarrollar algunos de los patrones aquí expuestos analizando su utilidad, virtudes y defectos.

Referencias:
Design Patterns (Erich Gamma, Richard Helm, Ralph Johnson, Hohn Vlissides)
Professional Java Server Programming J2EE, 1.3 Edition (Subrahmanyam Allamaraju, Cedric Beust, Marc Wilcox, Sameer Tyagi, Rod Johnson, Gary Watson, Alan Williamson, John Davies, Ramesh Nagappan, Andy Longshaw, P. G. Sarang, Tyler Jewell, Alex Toussaint)
Patrones de diseño en Wikipedia
Introducción al diseño con patrones por Manuel Lagos Torres

martes, 15 de noviembre de 2011

Patron Singleton

El patrón de diseño singleton (instancia única) está diseñado para restringir la creación de objetos pertenecientes a una clase o el valor de un tipo a un único objeto.
Su intención consiste en garantizar que una clase sólo tenga una instancia y proporcionar un punto de acceso global a ella.

El patrón singleton se implementa creando en nuestra clase un método que crea una instancia del objeto sólo si todavía no existe alguna. Para asegurar que la clase no puede ser instanciada nuevamente se regula el alcance del constructor (con atributos como protegido o privado).

La instrumentación del patrón puede ser delicada en programas con múltiples hilos de ejecución. Si dos hilos de ejecución intentan crear la instancia al mismo tiempo y esta no existe todavía, sólo uno de ellos debe lograr crear el objeto. La solución clásica para este problema es utilizar exclusión mutua en el método de creación de la clase que implementa el patrón.

Las situaciones más habituales de aplicación de este patrón son aquellas en las que dicha clase controla el acceso a un recurso físico único (como puede ser el ratón o un archivo abierto en modo exclusivo) o cuando cierto tipo de datos debe estar disponible para todos los demás objetos de la aplicación.
El patrón singleton provee una única instancia global gracias a que:
  • La propia clase es responsable de crear la única instancia.
  • Permite el acceso global a dicha instancia mediante un método de clase.
  • Declara el constructor de clase como privado para que no sea instanciable directamente.

Ejemplo aplicado a una clase

public class Fabrica {

          public static Fabrica instance;
           public static Fabrica getInstance() {
                 if (instance == null) {
                    return instance = new Fabrica();
            }
              return instance;
      }
         public String getMensaje() {
              return "Singleton";
           }
}
TestFabrica.java

public class TestFabrica {
       public static void main(String[] args) {
               String mensaje = Fabrica.getInstance().getMensaje();
               System.out.println(mensaje);
           }
  }

viernes, 30 de septiembre de 2011

17.- Interfaces


Una interfaz es una especie de plantilla para la construcción de clases. Normalmente una interfaz se compone de un conjunto de declaraciones de cabeceras de métodos (sin implementar, de forma similar a un método abstracto) que especifican un protocolo de comportamiento para una o varias clases. Además, una clase puede implementar una o varias interfaces: en ese caso, la clase debe proporcionar la declaración y definición de todos los métodos de cada una de las interfaces o bien declararse como clase abstract. Por otro lado, una interfaz puede emplearse también para declarar constantes que luego puedan ser utilizadas por otras clases.

Una interfaz puede parecer similar a una clase abstracta, pero existen una serie de diferencias entre una interfaz y una clase abstracta:

  • Todos los métodos de una interfaz se declaran implícitamente como abstractos y públicos. 
  • Una clase abstracta no puede implementar los métodos declarados como abstractos, una interfaz no puede implementar ningún método (ya que todos son abstractos). 
  • Una interfaz no declara variables de instancia.
  • Una clase puede implementar varias interfaces, pero sólo puede tener una clase ascendiente
    directa.
  •  Una clase abstracta pertenece a una jerarquía de clases mientras que una interfaz no pertenece a una jerarquía de clases. En consecuencia, clases sin relación de herencia pueden implementar la misma interfaz.
Declaración de una interfaz


La declaración de una interfaz es similar a una clase, aunque emplea la palabra reservada interface en lugar de class y no incluye ni la declaración de variables de instancia ni la implementación del cuerpo de los métodos (sólo las cabeceras). La sintaxis de declaración de una interfaz es la siguiente:

                    public interface IdentificadorInterfaz {
                      // Cuerpo de la interfaz . . .
                      }

Una interfaz declarada como public debe ser definida en un archivo con el mismo nombre de la interfaz y con extensión .java. Las cabeceras de los métodos declarados en el cuerpo de la interfaz se separan entre sí por caracteres de punto y coma y todos son declarados implícitamente como public y abstract (se pueden omitir). Por su parte, todas las constantes incluidas en una interfaz se declaran implícitamente como public, static y final (también se pueden omitir) y es necesario inicializarlas en la misma sentencia de declaración.

Por ejemplo, la interfaz Modificacion declara la cabecera de un único método:


/**
* Declaracion de la interfaz Modificacion
*/
        public interface Modificacion {
          void incremento(int a);
         }
que se almacena en el archivo fuente Modificacion.java y que, al compilarse:

$>javac Modificacion.java

genera un archivo Modificacion.class. Al no corresponder a una clase que implementa un método main, este archivo no puede ejecutarse con el intérprete de Java.

Segundo ejemplo: la interfaz constantes declara dos constantes reales con el siguiente

código fuente:
/**
* Declaracion de la interfaz Constantes
*/
                  public interface Constantes {
                       double valorMaximo = 10000000.0;
                       double valorMinimo = -0.01;
                   }

que se almacena en el archivo fuente Constantes.java y que, al compilarse, genera un archivo Constantes.class

Tercer ejemplo: la interfaz Numerico declara una constante real y dos cabeceras de
métodos con el siguiente código fuente:
/**
* Declaracion de la interfaz Numerico
*/

            public interface Numerico {
               double EPSILON = 0.000001;
               void establecePrecision(float p);
               void estableceMaximo(float m);
                 }

que se almacena en el archivo fuente Numerico.java y que, al compilarse, genera un archivo
Numerico.class.


 Implementación de una interfaz en una clase

Para declarar una clase que implemente una interfaz es necesario utilizar la palabra reservada implements en la cabecera de declaración de la clase. Las cabeceras de los métodos (identificador y número y tipo de parámetros) deben aparecer en la clase tal y como aparecen en la interfaz implementada. Por ejemplo, la clase Acumulador implementa la interfaz Modificacion y por lo tanto debe declarar un método incremento:

/**
* Declaracion de la clase Acumulador
*/
                   public class Acumulador implements Modificacion {
                           private int valor;
                          public Acumulador (int i) {
                         valor = i;
        }

                 public int daValor () {
                        return valor;
                 }

                 public void incremento (int a) {
                         valor += a;
                   }
  }

Esta cabecera con la palabra implements... implica la obligación de la clase Acumulador de definir el método incremento declarado en la interfaz Modificacion. El siguiente código muestra un ejemplo de uso de la clase Acumulador.

/**
* Demostracion de la clase Acumulador
*/

                    public class PruebaAcumulador {
                          public static void main (String [] args) {
                          Acumulador p = new Acumulador(25);
                          p.incremento(12);
                          System.out.println(p.daValor());
                      }
    }

La compilación y posterior ejecución del código anterior origina la siguiente salida por pantalla:
$>javac PruebaAcumulador.java
$>java PruebaAcumulador
37

La clase Acumulador tendría también la posibilidad de utilizar directamente las constantes declaradas en la interfaz si las hubiera.
Para poder emplear una constante declarada en una interfaz, las clases que no implementen esa interfaz deben anteponer el identificador de la interfaz al de la constante.


Jerarquía entre interfaces

La jerarquía entre interfaces permite la herencia simple y múltiple. Es decir, tanto la declaración de una clase, como la de una interfaz pueden incluir la implementación de otras interfaces. Los identificadores de las interfaces se separan por comas. Por ejemplo, la interfaz Una implementa otras dos interfaces: Dos y Tres.

             public interface Una implements Dos, Tres {
                   // Cuerpo de la interfaz . . .
                 }

Las clases que implementan la interfaz Una también lo hacen con Dos y Tres.
Otro ejemplo: pueden construirse dos interfaces, Constantes y Variaciones, y una clase, Factura, que las implementa:

         // Declaracion de la interfaz Constantes
            public interface Constantes {
                   double valorMaximo = 10000000.0;
                   double valorMinimo = -0.01;
       }

         // Declaracion de la interfaz Variaciones
          public interface Variaciones {
                        void asignaValor(double x);
                        void rebaja(double t);
        }

          // Declaracion de la clase Factura
            public class Factura implements Constantes, Variaciones {
                      private double totalSinIVA;
                      public final static double IVA = 0.16;

          public double sinIVA() {
              return totalSinIVA;
          }

          public double conIVA() {
                return totalSinIVA * (1+IVA);
            }

        public void asignaValor(double x) {
                 if (valorMinimo<x) totalSinIVA=x;
                 else totalSinIVA=0;
            }

            public void rebaja(double t) {
            totalSinIVA *= (1-t/100);
}
                 public static void main (String [] args) {
                          factura a = new factura();
                          a.asignaValor(250.0);
                          System.out.println("El precio sin IVA es: " + a.sinIVA());
                          System.out.println("El precio con IVA es: " + a.conIVA());
                          System.out.println("Rebajado durante el mes de mayo un 20%");
                          a.rebaja(20);
                          System.out.println("Rebajado sin IVA es: " + a.sinIVA());
                          System.out.println("Rebajado con IVA es: " + a.conIVA());
                        }
  }

Si una interfaz implementa otra, incluye todas sus constantes y declaraciones de métodos, aunque puede  redefinir tanto constantes como métodos.

Nota: Es peligroso modificar una interfaz ya que las clases dependientes dejan de funcionar hasta que éstas implementen los nuevos métodos.

Una clase puede simultáneamente descender de otra clase e implementar una o varias interfaces. En este caso la seccion implements se coloca a continuación de extends en la cabecera de declaración de la clase. 

Por ejemplo:
                 public class ClaseDescendiente extends ClaseAscendiente implements Interfaz {
                     ...
                 }

Utilización de una interfaz como un tipo de dato

Al declarar una interfaz, se declara un nuevo tipo de referencia. Pueden emplearse identificadores de interfaz en cualquier lugar donde se pueda utilizar el identificador de un tipo de dato (o de una clase). El objetivo es garantizar la sustituibilidad por cualquier instancia de una clase que la implemente. Por ejemplo, puede emplearse como tipo de un parámetro de un método:

      public class Calculos {
      public void asignacion(Variaciones x); {
         . . .
               }
    }

Sólo una instancia de una clase que implemente la interfaz puede asignarse al parámetro cuyo tipo corresponde al identificador de la interfaz. Esta facultad se puede aprovechar dentro la propia
interfaz. 

Por ejemplo:

            public interface Comparable {
                 // La instancia que llama a esMayor (this) y el parametro otra
                 // deben ser de la misma clase o de clases que implementen esta interfaz
                // La funcion devuelve 1, 0, -1 si this es mayor, igual o menor que otra

        public int esMayor(Comparable otra);
     }

En algún caso puede ser útil declarar una interfaz vacía como, por ejemplo:
public interface Marcador {
                }

Esta declaración es totalmente válida ya que no es obligatorio incluir dentro de una interfaz la declaración de una constante o la cabecera de un método. La utilidad de estas interfaces reside en la posibilidad de ser empleadas como tipos de dato para especificar clases sin necesidad de obligar a éstas a implementar algún método en concreto. Una interfaz no es una clase pero se considera un tipo en Java y puede ser utilizado como tal.

16.- Tipos de Metodos

Un método es una abstracción de una operación que puede hacer o realizarse con un objeto. Una
clase puede declarar cualquier número de métodos que lleven a cabo operaciones de lo más variado con
los objetos. En esta sección los métodos se clasifican en dos grupos: los métodos de instancia y los
métodos de clase. Además se cierra el capítulo con los métodos de clase o estáticos de la clase Math.

Métodos de instancia

Las clases pueden incluir en su declaración muchos métodos o no declarar ninguno. Los métodos pueden clasificarse en métodos de instancia y métodos de clase. 
Los métodos de instancia operan sobre las variables de instancia de los objetos pero también tienen acceso a las variables de clase. La sintaxis de llamada a un método de instancia es:

            idReferencia.idMetodo(parametros);    // Llamada tipica a un metodo de instancia

Todas las instancias de una clase comparten la misma implementación para un método de instancia. La instancia que hace la llamada al método es siempre un parámetro o argumento implícito. Dentro de un método de instancia, el identificador de una variable de instancia hace referencia al atributo de la instancia concreta que hace la llamada al método (suponiendo que el identificador del atributo no ha sido ocultado por el de un parámetro).

En el ejemplo anterior en la declaración de la clase CuentaBancaria, los métodos saldo y transferencia son métodos de instancia.

                 public double saldo() {
                    return saldo;
                   }

                 public void transferencia( CuentaBancaria origen ) {
                   saldo += origen.saldo;
                   origen.saldo=0;
                 }

Ejemplos de llamada a estos métodos dentro de PruebaCuentaBancaria:

                       CuentaBancaria c1 = new CuentaBancaria();
                       CuentaBancaria c2 = new CuentaBancaria(20.0);
                       c1.transferencia(c2);
                       System.out.println("Cuenta con: " + c1.saldo() + " euros");

Métodos de clase

En principio, los métodos de clase no operan sobre las variables de instancia de los objetos.
Los métodos de clase pueden trabajar con las variables de clase pero no pueden acceder a las variables de instancia declaradas dentro de la clase, a no ser que se crea una nueva instancia y se acceda a las variables de instancia a través del nuevo objeto. Los métodos de clase también pueden ser llamados precediendolos con el identificador de la clase, sin necesidad de utilizar el de una instancia.

            IdClase.idMetodo(parametros); // Llamada tipica a un metodo de clase

La palabra static determina la declaración de un método de clase. Por defecto, si no se indica la palabra static, el método declarado se considera un método de instancia.
En el ejemplo anterior en la declaración de la clase CuentaBancaria, el método incCuentas es un método de clase.

                 public static void incCuentas () {
                     totalCuentas++;
                   }

Un ejemplo de llamada a este método dentro de PruebaCuentaBancaria sería:

                  CuentaBancaria.incCuentas();

Las diferencias entre los métodos de instancia y los de clase se resumen en la Tabla


Los métodos de clase o estáticos se pueden considerar equivalentes a las rutinas (globales) de otros lenguajes de programación como Pascal o C. Como ejemplos típicos de métodos estáticos pueden indicarse los métodos de Java correspondientes a las funciones matemáticas sin, cos, exp, pow... de la clase java.lang.Math (Tabla 14.2). Las llamadas a estos métodos se realizan anteponiendo el identificador de la clase Math al identificador del método: Math.sin(angulo)…

miércoles, 14 de septiembre de 2011

15.- Devolución de Valores y Sobrecarga de Métodos

Devolución de Valores


Los métodos pueden devolver valores básicos (int, short, double, etc.), Strings, arrays e incluso objetos.
En todos los casos es el comando return el que realiza esta labor. En el caso de arrays y objetos, devuelve una referencia a ese array u objeto. 
Ejemplo:

     class FabricaArrays {
                public int[] obtenArray(){
                int array[]= {1,2,3,4,5};
                return array;
                   }
}

      public class returnArray {
                public static void main(String[] args) {
                FabricaArrays fab=new FabricaArrays();
                int nuevoArray[]=fab.obtenArray();
                   }
}

Sobrecarga de Métodos

Una propiedad de la POO es el polimorfismo. Java posee esa propiedad ya que admite 
sobrecargar los métodos. Esto significa crear distintas variantes del mismo método.

Ejemplo:

      class Matemáticas{
               public double suma(double x, double y) {
               return x+y;
               }

         public double suma(double x, double y, double z){
               return x+y+z;
         }

          public double suma(double[] array){
               double total =0;
               for(int i=0; i<array.length;i++){
               total+=array[i];
         }
               return total;
}



La clase matemáticas posee tres versiones del método suma. una versión que suma dos números double, otra que suma tres y la última que suma todos los miembros de un array de doubles. Desde el código se puede utilizar cualquiera de las tres versiones según convenga.

14.- Argumentos por valor y por referencia

En todos los lenguajes éste es un tema muy importante. Los argumentos son los datos
que recibe un método y que necesita para funcionar. Ejemplo:


      public class Matemáticas {
                    public double factorial(int n){
                    double resultado;
                    for (resultado=n;n>1;n--) resultado*=n;
                    return resultado;
             }
                   ...
     public static void main(String args[]){
                   Matemáticas m1= new Matemáticas();
                   double x=m1.factorial(25);//Llamada al método
             }

En el ejemplo anterior, el valor 25 es un argumento requerido por el método factorial para que éste devuelva el resultado (que será el factorial de 25). En el código del método factorial, este valor 25 es copiado a la variable n, que es la encargada de almacenar y utilizar este valor.

Se dice que los argumentos son por valor, si la función recibe una copia de esos datos, es decir la variable que se pasa como argumento no estará afectada por el código.

Ejemplo:




     class prueba {
                    public void metodo1(int entero){
                    entero=18;
                    ...
                }
                    ...

    public static void main(String args[]){
                    int x = 24;
                    prueba miPrueba = new prueba();
                    miPrueba.metodo1(x);
                    System.out.println(x);           //Escribe 24, no 18
       }


Este es un ejemplo de paso de parámetros por valor. La variable x se pasa como argumento o parámetro para el método metodo1, allí la variable entero recibe una copia del valor de x en la variable entero, y a esa copia se le asigna el valor 18. Sin embargo la variable x no está afectada por esta asignación.
Sin embargo en este otro caso:


     class prueba {
                    public void metodo1(int[] entero){
                       entero[0]=18;
                    ...
                    }
                    ...
     public static void main(String args[]){
                    int x[]={24,24};
                    prueba miPrueba = new prueba();
                    miPrueba.metodo1(x);
                    System.out.println(x[0]); //Escribe 18, no 24


Aquí sí que la variable x está afectada por la asignación entero[0]=18. La razón es porque en este caso el método no recibe el valor de esta variable, sino la referencia, es decir la dirección física de esta variable. entero no es una replica de x, es la propia x llamada de otra forma.
Los tipos básicos (int, double, char, boolean, float, short y byte) se pasan por valor. También se pasan por valor las variables String. Los objetos y arrays se pasan por referencia.

13.- Creación de clases

Definir atributos de la clase (variables, propiedades o datos de la clases)



Cuando se definen los datos de una determinada clase, se debe indicar el tipo de propiedad que es (String, int, double, int[][],...) y el especificador de acceso (public, private,...). El especificador indica en qué partes del código ese dato será visible.

Ejemplo:
                    class Persona {
                                  public String nombre;    //Se puede acceder desde cualquier clase
                                  private int contraseña;   //Sólo se puede acceder desde la clase Persona
                                  protected String dirección;    //Acceden a esta propiedad esta clase y sus 
                                                                              //descendientes

Por lo general las propiedades de una clase suelen ser privadas o protegidas, a no ser que se trate de un valor constante, en cuyo caso se declararán como públicos.
Las variables locales de una clase pueden ser inicializadas.

             class auto{
                              public nRuedas=4;

Ilustración 8, La clase persona en UML. El signo + significa public,
el signo # protected y el signo - private




Definir métodos de clase (operaciones o funciones de clase)

Un método es una llamada a una operación de un determinado objeto. Al realizar esta llamada (también se le llama enviar un mensaje), el control del programa pasa a ese método y lo mantendrá hasta que el método finalice o se haga uso de return. Para que un método pueda trabajar, normalmente hay que pasarle unos datos en forma de argumentos o parámetros, cada uno de los cuales se separa por comas.

Ejemplos de llamadas:

                                balón.botar();                 //sin argumentos
                                miCoche.acelerar(10);
                                ficha.comer(posición15);  //posición 15 es una variable que se pasa como argumento
                                partida.empezarPartida(“18:15”,colores);

Los métodos de la clase se definen dentro de ésta. Hay que indicar un modificador de acceso (public, private, protected o ninguno, al igual que ocurre con las variables y con la propia clase) y un tipo de datos, que indica qué tipo de valores devuelve el método.
Esto último se debe a que los métodos son funciones que pueden devolver un determinado valor (un entero, un texto, un valor lógico,...) mediante el comando return. Si el método no devuelve ningún valor, entonces se utiliza el tipo void que significa que no devuelve valores (en ese caso el método no tendrá instrucción return).
El último detalle a tener en cuenta es que los métodos casi siempre necesitan datos para realizar la operación, estos datos van entre paréntesis y se les llama argumentos. Al definir el método hay que indicar que argumentos se necesitan y de qué tipo son.

Ejemplo:

      public class vehiculo {
                            /** Función principal */
                                int ruedas;
                                private double velocidad=0;
                                 String nombre;
                            /** Aumenta la velocidad*/
                            public void acelerar(double cantidad) {
                               velocidad += cantidad;
                            }
                            ** Disminuye la velocidad*/
                            public void frenar(double cantidad) {
                               velocidad -= cantidad;
                            }
                            /** Devuelve la velocidad*/
                            public double obtenerVelocidad(){
                               return velocidad;
                            }
                            public static void main(String args[]){
                                  vehiculo miCoche = new vehiculo();
                                  miCoche.acelerar(12);
                                  miCoche.frenar(5);
                                  System.out.println(miCoche.obtenerVelocidad());
                            } // Da 7.0


En la clase anterior, los métodos acelerar y frenar son de tipo void por eso no tienen sentencia return. Sin embargo el método obtenerVelocidad es de tipo double por lo que su resultado es devuelto por la sentencia return y puede ser escrito en pantalla.

Ilustración 9, Versión UML de la clase
Coche