3.10 Diseño de programas OO Patrones de diseño Patrones de diseño l l l Factory l l l Son formas convenientes de reutilizar código orientado a objetos entre proyectos y programadores Promueven que los objetos se ocupen solo de “sus propios asuntos”. Un patrón aborda un problema de diseño recurrente que aparece en situaciones especificas y que represente una solución a este. 1º Ejemplo de Factory (1/4) Una “fábrica” es un patrón que devuelve una instancia de una clase entre un conjunto de posibilidades dependiendo de la información proveída. Generalmente devuelve una clase abstracta o una interfaz. El programador no sabe que instancia específica esta usando. l l Clase para separar un nombre en dos partes. Clase base con dos hijas. abstract class Namer{ protected String first; protected String last; public String getFirst() { return first; } public String getLast() { return last; } } 1º Ejemplo de Factory (2/4) class FirstFirst extends Namer{ class LastFirst extends Namer{ public FirstFirst(String s) { public LastFirst(String s) { int i = s.lastIndexOf(" " ); int i = s.indexOf("," ); i f (i > 0 ) { i f (i > 0 ) { first=s.substring(0, i).trim(); last=s.substring(0,i).trim(); last=s.substring(i + 1).trim(); first=s.substring(i+1).trim(); } else { public class NameFactory { public Namer getNamer(String entry) { int i = entry.indexOf( ","); if (i > 0) } else { first = " "; last = s; last = s; first = " "; } return new LastFirst(entry); else return new FirstFirst(entry); } } } 1º Ejemplo de Factory (3/4) } } } } 1 1º Ejemplo de Factory (4/4) 2º Ejemplo de Factory (1/4) public static void main(String[] args) { l NameFactory factory = new NameFactory(); l Namer namer ; Fabrica de objetos para realizar loggin Utilización de configuraciones por defecto interface Logger{ namer = factory.getNamer("Perez, Juanito") ; public void doLog(String message, int level); System.out.println( "First: " + namer.getFirst()); } System.out.println( "Last: " + namer.getLast()); abstract class AsbtractLogger implements Logger{ private int minLevel; namer = factory.getNamer("Maria Espinoza") ; protected AsbtractLogger( int level){ minLevel=level; System.out.println( "First: " + namer.getFirst()); } System.out.println( "Last: " + namer.getLast()); public void doLog(String message, int level){ } if(level>=minLevel) First: Juanito doLog(message); Last: Perez } First: Maria abstract protected void doLog(String message); } Last: Espinoza 2º Ejemplo de Factory (3/4) 2º Ejemplo de Factory (2/4) public class LoggerFactory { private int defaultLevel=0; private String defaultDestiny; class NetLogger extends AsbtractLogger{ public void setDefaultLevel(int l){defaultLevel=l;} public NetLogger(URL destiny, int minLevel){ public void setDefaultDestiny(String str) {defaultDestiny = str;} super (minLevel); public Logger createLogger(){return createLogger(defaultDestiny);} public Logger createLogger(String destiny){ } try{ protected void doLog(String message) { URL url = new URL(destiny); System.out.println("[NetLogging]" +message); return new NetLogger(url, defaultLevel); } }catch(MalformedURLException mue){} } try{ class FileLogger extends AsbtractLogger{ File file = new File(destiny); if(file.exists()) public FileLogger(File destiny, int minLevel){ return new FileLogger(file,defaultLevel); super (minLevel); else } throw new FileNotFoundException(); protected void doLog(String message) { }catch(FileNotFoundException fnfe){} System.out.println("[FileLogging]"+message); return null; } } } } 2º Ejemplo de Factory (3/4) Singleton public static void main(String[] args) { l LoggerFactory factory = new LoggerFactory(); factory.setDefaultLevel(2); [NetLogging]test2 factory.setDefaultDestiny("http://log.inf.utfsm.cl") ; [NetLogging]test3 Logger logger1= factory.createLogger(); [NetLogging]test4 [FileLogging]test2 logger1.doLog("test1" , 1 ) ; [FileLogging]test3 logger1.doLog("test2" , 2 ) ; logger1.doLog("test3" , 3 ) ; [FileLogging]test4 l Un “singleton” es un objeto que requiere tener una sola instancia creada de manera simultanea. Útil para mejorar rendimiento y asegurar recursos no compartidos. logger1.doLog("test4" , 4 ) ; Logger logger2 = factory.createLogger("test.log" ); logger2.doLog("test1" , 1 ) ; logger2.doLog("test2" , 2 ) ; logger2.doLog("test3" , 3 ) ; logger2.doLog("test4" , 4 ) ; } 2 1º Ejemplo de Singleton (1/3) 1º Ejemplo de Singleton (2/3) public class PrintSpooler { Spooler de impresión. Solo se permite un numero máximo de instancias. l l private static i n t instances=0; private static i n t maxInstances=2; private PrintSpooler(){} public static PrintSpooler newSpooler(){ if(instances>=maxInstances) return null; else{ instances++; return n e w PrintSpooler(); } } public void finalize(){ instances --; } } 1º Ejemplo de Singleton (3/3) public static void main(String[] args) { PrintSpooler ps1,ps2, ps3; 2º Ejemplo de Singleton (1/3) l l ps1 = PrintSpooler.newSpooler(); if(ps1!=null) System.out.println( "First spooler created" ); l Conexión a la base de datos Una sola instancia Se puede mezclar con una fabrica else System.out.println("First spooler not created!" ); ps2 = PrintSpooler.newSpooler(); if(ps2!=null) System.out.println( "Second spooler created"); else System.out.println("Second spooler not created" ); ps3 = PrintSpooler.newSpooler(); if(ps3!=null) System.out.println( "Third spooler created" ); else System.out.println("Third spooler not created") ; } 2º Ejemplo de Singleton (2/3) 2º Ejemplo de Singleton (3/3) public class DBConnection { private static DBConnection instance=null; private static String defaultSetup; public static void main(String[] args) { DBConnection dbc1, dbc2; private String setup; DBConnection.setParameters("jdbc:oracle:thin:bd.inf.utfsm.cl/db1"); private DBConnection(String s){setup=s;} dbc1 = DBConnection.getConnection(); public static void setParameters(String str){defaultSetup=str;}; dbc2 = DBConnection.getConnection(); public static DBConnection getConnection(){ if(instance== null ) if(dbc1==dbc2) instance = n e w DBConnection(defaultSetup); System.out.println("Only one connection" ); return instance; else } System.out.println("More than one connection"); public void finalize(){ instance= null ; } } } 3 Patrones de diseño empresariales l l l Principalmente para aplicaciones distribuidas. Incluyen conceptos de J2EE. Mantienen complejidad baja y alta mantenibilidad en sistemas web integrados con un back-end. View Helper l Motivación l l l l Los cambios en las capas de presentación son usuales. Son difíciles de desarrollar y mantener cuando la lógica de acceso a datos del negocio y la lógica de formatos de presentación están mezclados. Se requiere una separación clara de las tareas Solución l Una vista (View) contiene el código de formato, delegando las responsabilidades de procesamiento a una clase que la asiste (Helper). View Helper View Helper Session Facade Session Facade l l Motivación l La lógica y los datos del negocio quedan expuestos a los clientes a través de componente empresariales. l Esto implica que la capa cliente queda expuesta a la complejidad de las interfaces de los servicios distribuidos l Alto acoplamiento, provocando dependencias entre las capas del cliente y de la lógica. l Se generan muchas invocaciones de métodos entre el cliente y el servidor, generando problemas de rendimiento en la red. l No existe una estrategia uniforme de acceso desde los clientes h acia los servicios, permitiendo un mal uso de las interfaces de los componentes. Solución l Utilizar un componente en el servidor como una fachada que encapsula la complejidad de las interacciones entre los componentes empresariales y el cliente, entregándole a este ultimo una estra tegia de acceso uniforme de menor granularidad. 4 Session Facade Data Access Object Motivación l Las aplicaciones requieren de datos persistentes l Además pueden requerir acceder a información que reside en distintos sistemas. l Los componentes requieren un acceso transparente al lugar de almacenamiento persistente o a la implementación específica de la fuente de datos. Solución l Utilizar un objeto de acceso a datos (Data Access Object, DAO) para abstraer y encapsular el acceso a la fuente de datos. El DAO administra la conexión con la fuente de datos para recuperar y almacenar los datos. l l Data Access Object Data Access Object Ejemplo completo l DAO para recuperar productos e usuarios l l l l l l DAO para usuarios XML Base de datos Web service Componentes del negocio para listar y vender productos Fachada para encapsular funcionalidad Cliente que accede a los servicios public class User { private int rut; private String nombre; public User(String n){setNombre(n);} public int getPK(){ return rut; } public void setNombre(String n){nombre=n; } } 5 DAO para usuarios DAO para usuarios class UserDAO { public static void main(String args []){ private User user ; public UserDAO(User u){user = u;} User u = new User("111111"); public boolean load() throws SQLException{ UserDAO dao = new UserDAO(u); int rut = this.user.getPK (); try{ user.setNombre("pedrito") ; dao.load (); return false; } u.setNombre(" Juanito"); public boolean store() throws SQLException{ dao.store(); int rut = this.user.getPK (); }catch(SQLException sqle ){} return false; } } public static Collection getUsers (){ return null; } } DAO para productos DAO para productos class ProductXMLDAO extends ProductDAO{ public Product[] getProducts( ) {return null; } public Product[] getFilteredProducts(String string) {return null ;} public class Product { public i n t getStock(Product p ) {return 0;} private String code; public void decrementStock(Product p ) {} public String getCode(){ } return code; class ProductDBDAO extends ProductDAO{ } public Product[] getProducts( ) {return null; } public Product[] getFilteredProducts(String string) {return null ;} } public i n t getStock(Product p ) {return 0;} public void decrementStock(Product p ) {} abstract class ProductDAO{ public abstract Product[] getProducts (); } public abstract Product[] getFilteredProducts(String str); class ProductWSDAO extends ProductDAO{ public Product[] getProducts( ) {return null; } public abstract int getStock(Product p); public Product[] getFilteredProducts(String string) {return null ;} public abstract void decrementStock(Product p); public i n t getStock(Product p ) {return 0;} } public void decrementStock(Product p ) {} } DAO para productos Componentes del negocio Factory class ProductDAOFactory{ l private static String source= "jdbc:postgresql:localhost:5432/myDb"; private static ProductDAO instance=null; public static ProductDAO createProductDAO(){ Singleton if(instance!= null ) return instance; if(source.equals( "source.xml" )) l Un componente para agrupar tareas de recuperación de información. Un componente para agrupar tareas de modificación de información. instance = n e w ProductXMLDAO(); else if(source.equals ("jdbc:postgresql:localhost :5432/myDb") ) instance = n e w ProductDBDAO( ) ; else if(source.equals ("http: / /localhost:8081/ws" )) instance = n e w ProductWSDAO( ) ; return instance; } } 6 class ProductRetriever{ ProductDAO pDao = null; Componentes del negocio public ProductRetriever( ) { Componentes del negocio pDao = ProductDAOFactory.createProductDAO(); } public Product getProduct(String code){ Product[] arr= pDao.getFilteredProducts( "code="+code ); class ProductManager{ if(arr.length >0) return arr[0]; ProductDAO pDao = null; else public ProductManager(){ throw new IllegalArgumentException pDao = ProductDAOFactory.createProductDAO (); ("The product code doesn't exist! "); } } public Collection getProducts (){ public boolean dispatchProduct(Product p, User u){ Product[] arr= pDao.getProducts( ) ; if(pDao.getStock(p)>0) ... pDao.decrementStock(p); } public Collection getUserProducts(User u){ ... Product[] arr = pDao.getFilteredProducts ("buyer= "+u.getPK()); } ... } } } Fachada de listado y venta: Fachada de listado y venta Listado public class ProductFacade { private ProductRetriever retriever=new ProductRetriever(); public ProductBean listProducts(){ private ProductManager manager = new ProductManager(); ProductBean bean = new ProductBean (); private User loggedUser; bean.setProducts(retriever.getProducts()); public ProductBean listProducts(){ bean.setUserProducts(retriever.getUserProducts(loggedUser )); ... } return bean; } public boolean sellProduct(String pCode, int quantity ){ ... } public static class ProductBean{ ... } } Fachada de listado y venta: Fachada de listado y venta: Venta Objeto de transferencia public static class ProductBean{ private Collection products; private Collection userProducts; public boolean sellProduct(String pCode, int quantity ){ public Collection getProducts () { Product p; return products; try{ } p=retriever.getProduct(pCode); public Collection getUserProducts () { }catch (IllegalArgumentException iae){ return userProducts; return false; } public void setProducts(Collection collection) { } products = collection ; manager.dispatchProduct(p, loggedUser ); } return true; public void setUserProducts(Collection collection) { } userProducts = collection ; } } 7 Cliente Cliente public class Client { View ClientView view; class ClientHelper{ public void showProducts(){ private ProductFacade facade = new ProductFacade(); view.showProducts(); public Collection getProducts(){ } Helper } Collection c = new Vector(); ProductBean bean = facade.listProducts(); class ClientView{ c.addAll(bean.getUserProducts()); private ClientHelper helper = new ClientHelper( ) ; c.addAll(bean.getProducts()); public void showProducts(){ return c ; Iterator it = helper.getProducts().iterator(); while (it.hasNext()) System.out.println(it.next()); } } } } Conclusiones l l l Clara separación de tareas. Posibilidad de distribuir objetos. Definición de paquetes l l l Las únicas clases publicas son “User”, “Product” y “ProductFacade”. El cliente se define en un paquete distinto. El cliente solo puede acceder a la fachada. Resumen l l l l l Factory Singleton View Helper Session Facade Data Access Object 8