Download 3.5 Patrones básicos para favorecer la eficiencia, la
Document related concepts
no text concepts found
Transcript
3.5 Patrones básicos para favorecer la eficiencia, la escalabilidad y el mantenimiento Introducción n Parámetros en conflicto en todo diseño n n n n n Eficiencia Mantenimiento Escalabilidad Un buen diseño balancea bien los tres parámetros Patrones n n n n Operaciones gruesas (“Fat operations”) Modelos de objetos gruesos (“Coarse object models”) Diseño por capas (“Layers”) Otros Ejemplo: control de temperatura – descripción (1) Red de termostatos Servidor Intranet Aplicación de administración Ejemplo: control de temperatura – descripción (y 2) n Cada termostato dispone de memoria para almacenar: n n n n Identificador (sólo lectura) Modelo (sólo lectura) Temperatura (sólo lectura) Temperatura nominal (lectura/escritura) n n Cada termostato dispone de un rango válido de valores Ubicación (lectura/escritura) IDL para el control de temperatura (1) module es { module udc { module fbellas { module corbaws { module idl { module tcs { typedef String DeviceIdentifier; typedef string Model; typedef short Temperature; typedef string Location; exception MalfunctioningException { ... } exception TemperatureOutOfRangeException { ... } exception ThermostatNotFoundException { ... } interface Thermostat { DeviceIdentifier getDeviceIdentifier() raises(MalfunctioningException); Model getModel() raises(MalfunctioningException); Temperature getTemperature() raises(MalfunctioningException); Temperature getNominalTemperature() raises(MalfunctioningException); Location getLocation() raises(MalfunctioningException); void setNominalTemperature (in Temperature value) raises(TemperatureOutOfRangeException, MalfunctioningException); void setLocation(in Location aLocation) raises(MalfunctioningException); }; IDL para el control de temperatura (y 2) interface Controller { typedef sequence<Thermostat> ThermostatList; Thermostat findByIdentifier(in DeviceIdentifier identifier) raises(ThermostatNotFoundException, MalfunctioningException); ThermostatList findByLocation(in Location aLocation) raises(MalfunctioningException); }; }; }; }; }; }; }; Comentarios n Localizar un termostato e imprimir su estado n n 6 operaciones remotas (localización + 5 operaciones get) Demasiadas llamadas remotas :Console :Controller :Thermostat findByIdentifier() getDeviceIdentifier() getModel() getTemperature() getNominalTemperature() getLocation() Red Red (potencialmente) Operaciones gruesas (1) struct ThermostatState { DeviceIdentifier fIdentifier; Model fModel; Temperature fTemperature; Temperature fNominalTemperature; Location fLocation; }; interface Thermostat { ThermostatState getState() raises(MalfunctioningException); void setNominalTemperature (in Temperature value) raises(TemperatureOutOfRangeException, MalfunctioningException); void setLocation(in Location aLocation) raises(MalfunctioningException); }; Operaciones gruesas (y 2) interface Controller { struct ThermostatListItem { Thermostat fThermostat; ThermostatState fState; }; typedef sequence<ThermostatListItem> ThermostatList; ThermostatListItem findByIdentifier(in DeviceIdentifier identifier) raises(ThermostatNotFoundException, MalfunctioningException); ThermostatList findByLocation(in Location aLocation) raises(MalfunctioningException); }; Operaciones gruesas – evaluación (1) Minimiza el número de invocaciones remotas n n Localizar un termostato e imprimir su estado: 1 invocación remota (si los objetos Controller y Thermostat están en la misma máquina) o 2 (en otro caso) :Console :ThermostatListItem :Controller findByIdentifier() :Thermostat getState() fState.fIdentifier fState.fModel fState.fTemperature fState.fNominalTemperature fState.fLocation En la misma máquina virtual Red Red (potencialmente) Operaciones gruesas – evaluación (2) n n En otras aplicaciones también habrá operaciones set para dar valor (de golpe) a todos los atributos modificables Herencia <<interface>> 1 Controller gestiona * <<interface>> Thermometer getState() : ThermometerState setLocation(l: Location) : void <<interface>> Thermostat setNominalTemperature(t : Temperature) : void Operaciones gruesas – evaluación (y 3) n Herencia (cont) n getState devuelve ThermometerState n n ThermometerState: fIdentifier, fModel, fTemperature y fLocation Idealmente nos gustaría definir el struct ThermostatState, de manera que este último derive del primero y añada fNominalTemperature n n n El objeto que implementa Thermostat, devolvería un ThermostatState Problema: Un struct no puede heredar de otro CORBA 2.3 añadió OBV (Objects-By-Value) n Permite definir tipos valor con capacidad de herencia Modelos de objetos gruesos struct Thermostat { DeviceIdentifier fIdentifier; Model fModel; Temperature fTemperature; Temperature fNominalTemperature; Location fLocation; }; interface Controller { typedef sequence<Thermostat> ThermostatList; void setNominalTemperature(in DeviceIdentifier identifier, in Temperature value) raises(ThermostatNotFoundException, TemperatureOutOfRangeException, MalfunctioningException); void setLocation(in DeviceIdentifier identifier, Location aLocation) raises(ThermostatNotFoundException, MalfunctioningException); Thermostat findByIdentifier(in DeviceIdentifier identifier) raises(ThermostatNotFoundException, MalfunctioningException); ThermostatList findByLocation(in Location aLocation) raises (MalfunctioningException; }; Modelos de objetos gruesos – evaluación (1) n Minimiza el número de objetos remotos n Favorece la escalabilidad n n n Sin embargo, con cualquiera de los dos diseños anteriores y haciendo uso de las políticas del POA es posible mantener una caché de servants (patrón “Evictor”) o implementar todos los objetos Thermostat (patrón “Default Servant”) con un solo servant Minimiza el número de invocaciones remotas No favorece que los drivers de los termostatos puedan estar implementados en distintos lenguajes n En las soluciones anteriores, los objetos Thermostat eran objetos CORBA (que potencialmente pueden estar en procesos distintos e implementados con lenguajes distintos) Modelos de objetos gruesos – evaluación (y 2) n Si existe más de un Controller, el identificador del termostato no es suficiente n n Herencia n n n Se precisa también una referencia a su Controller Se agrava el problema anterior Posible solución usando OBVs En general, esta solución es apropiada cuando los objetos no tienen comportamiento (sólo datos), en cuyo caso es mejor definirlos como struts (u OBVs) y no como objetos CORBA Diseño por capas Interfaz de usuario (no CORBA) Capa de traducción y delegación (CORBA) Capa de traducción y delegación (CORBA) IDL Cliente n Capa de lógica de negocio (no CORBA) Servidor Capa de delegación y traducción n n Muy pequeña en comparación con el resto del código de la aplicación Patrón Adapter Diseño por capas - ventajas n Minimiza errores n n n n Especialmente en mappings complejos (ej.: IDL a C++) Sólo unos pocos miembros del proyecto necesitan conocer CORBA La funcionalidad principal de la aplicación se puede probar de manera aislada de CORBA Facilita la migración a otro ORB (caso de problemas de portabilidad) y/o una versión superior Diseño por capas - inconvenientes n Añade más overhead n n Traducción de instancias de tipos generados por el compilador de IDL a instancias de tipos de más alto nivel y viceversa Requiere más código n Clases equivalentes a conceptos definidos en IDL Diseño por capas – Ejemplo Descripción n n Sólo existen termostatos El servidor tiene que gestionar termostatos de dos fabricantes (Acme1 y Acme2) n n Ambos proporcionan software Java para leer y modificar su estado (paquetes com.acme1.thermostats y com.acme2.thermostats) En el futuro puede existir más de un controlador, por distintos motivos n n Posibles fabricantes que proporcionen software de gestión de sus termostatos sólo para un lenguaje/plataforma particular Escalabilidad Diseño por capas – Ejemplo Diagrama de paquetes del software de los fabricantes com acme1 acme2 thermostats thermostats Diseño por capas – Ejemplo com::acme1::thermostats y com::acme2::thermostats Acme1Driver + + + + + + enable(identifier : String) : void disable(identifier : String) : void get(identifier : String, attribute : String) : String set(identifier : String, attribute : String, value : String) : void getAllIdentifiers() : String[] exists(identifier : String) : boolean Acme2Driver + + + + + + online(identifier : String) : void offline(identifier : String) : void read(identifier : String, attribute : String) : String write(identifier : String, attribute : String, value : String) : void getAllIdentifiers() : java::util::Collection exists(identifier : String) : boolean Diseño por capas – Ejemplo Diagrama de paquetes de nuestro software es::udc::fbellas::corbaws tcs thermostats admin server util shell server shell commands proxies idlutil idl wrappers main CORBA main commands proxies admin wrappers IDL thermostats server exceptions Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::idl <<IDL struct>> <<IDL interface>> ThermostatState Thermostat <<return>> + fIdentifier : DeviceIdentifier + fModel : Model + getState() : ThermostatState + fTemperature : Temperature + setNominalTemperature(value : Temperature) : void + fNominalTemperature : Temperature + setLocation(aLocation : Location) : void + fLocation : Location 1 1 <<IDL struct>> ThermostatListItem + fThermostat : Thermostat + fstate : ThermostatState 0..n <<IDL sequence>> ThermostatList <<return>> <<IDL interface>> Controller + findByIdentifier(identifier : DeviceIdentifier) : ThermostatListItem + findByLocation(aLocation : Location) : ThermostatList Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::idl::Thermostat (1) #ifndef _es_udc_fbellas_corbaws_tcs_idl_Thermostat_ #define _es_udc_fbellas_corbaws_tcs_idl_Thermostat_ module es { module udc { module fbellas { module corbaws { module tcs { module idl { typedef typedef typedef typedef string DeviceIdentifier; string Model; short Temperature; string Location; struct ThermostatState { DeviceIdentifier fIdentifier; Model fModel; Temperature fTemperature; Temperature fNominalTemperature; Location fLocation; }; Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::idl::Thermostat (y 2) exception MalfunctioningException { string fExplanation; }; exception TemperatureOutOfRangeException { DeviceIdentifier fIdentifier; Temperature fMinimumTemperature; Temperature fMaximumTemperature; Temperature fSelectedTemperature; }; interface Thermostat { ThermostatState getState() raises(MalfunctioningException); void setNominalTemperature(in Temperature value) raises(TemperatureOutOfRangeException, MalfunctioningException); void setLocation(in Location aLocation) raises(MalfunctioningException); }; }; }; }; }; }; }; #endif Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::idl::Controller (1) #ifndef _es_udc_fbellas_corbaws_tcs_idl_Controller_ #define _es_udc_fbellas_corbaws_tcs_idl_Controller_ #include "Thermostat.idl" module es { module udc { module fbellas { module corbaws { module tcs { module idl { struct ThermostatListItem { Thermostat fThermostat; ThermostatState fState; }; typedef sequence<ThermostatListItem> ThermostatList; exception ThermostatNotFoundException { string fIdentifier; }; Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::idl::Controller (y 2) interface Controller { ThermostatListItem findByIdentifier( in DeviceIdentifier identifier) raises(ThermostatNotFoundException, MalfunctioningException); ThermostatList findByLocation(in Location aLocation) raises(MalfunctioningException); }; }; }; }; }; }; }; #endif Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::idlutil n CommonIDLTypeConversor n Clase utilidad con métodos para convertir tipos IDL a Java y viceversa, comunes a la herramienta de administración y al servidor Diseño por capas – Ejemplo es::udc::fbellas::corbaws::thermostats (1) <<Interface>> Driver DriverManager + getModel() : String - drivers : java.util.Map + register(driver : Driver) : void + unregister(model : String) : void 1 + drivers() : java.util.Iterator + get(model : String) : Driver manages + create(identifier : String, location : String, nominalTemperature : short) : void + destroy(identifier : String) + getTemperature(identifier : String) : short 0..n + getNominalTemperature(identifier : String) : short + setNominalTemperaure(identifier : String, value : short) : void + getLocation(identifier : String) : String + setLocation(identifier : String, location : String) : void + findAllIdentifiers() : java::util::Collection + exists(identifier : String) : boolean Acme1DriverAdapter Acme2DriverAdapter - model : String - model : String + Acme1DriverAdapter() + Acme2DriverAdapter() <<use>> Acme1Driver (from thermostats) <<use>> Acme2Driver (from thermostats) Diseño por capas – Ejemplo es::udc::fbellas::corbaws::thermostats (y 2) ThermostatState - identifier : String model : String temperature : short nominalTemperature : short location : String ThermostatState(identifier : String, model : String, temperature : short, nominalTemperature : short, location : String) + getIdentifier() : String + getModel() : String + getTemperature() : short + getNominalTemperature() : short + getLocation() : String + toString() : String Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::server::wrappers (1) ThermostatPOA ControllerPOA ThermostatImpl - driver : es::udc::fbellas::corbaws::thermostats::Driver ControllerImpl - identifier : String - thermostatImpls : java::util::Map - model : String - orb : org::omg::CORBA::ORB 1 0..n + ControllerImpl(orb : org::omg::CORBA::ORB) - nominalTemperature : short - location : String + ThermostatImpl(identifier : String, driver : es::udc::fbellas::corbaws::thermostats::Driver) <<use>> <<use>> DriverManager <<Interface>> (from thermostats) Driver (from thermostats) 1 Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::server::wrappers (y 2) WrappersFacade + run(args : String[]) : void Crea una instancia de "ControllerImpl", la registra en el servicio de nombres con el nombre "es/udc/fbellas/corbaws/tcs/controller", y se queda escuchando peticiones. Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::server::wrappers::ThermostatImpl (1) // ... ThermostatImpl(String identifier, Driver driver) throws es.udc.fbellas.corbaws.thermostats.ThermostatNotFoundException, es.udc.fbellas.corbaws.thermostats.MalfunctioningException { /* The temperature can not be cached. */ this.driver = driver; this.identifier = identifier; model = driver.getModel(); nominalTemperature = driver.getNominalTemperature(identifier); location = driver.getLocation(identifier); } Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::server::wrappers::ThermostatImpl (2) public ThermostatState getState() throws es.udc.fbellas.corbaws.tcs.idl.MalfunctioningException { try { /* "identifier" and "model" never change. */ ThermostatState state = new ThermostatState(); state.fIdentifier = identifier; state.fModel = model; synchronized(this) { /* * The temperature is the only attribute that can not be * cached. */ state.fTemperature = driver.getTemperature(identifier); state.fNominalTemperature = nominalTemperature; state.fLocation = location; } Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::server::wrappers::ThermostatImpl (3) return state; } catch (es.udc.fbellas.corbaws.thermostats.MalfunctioningException e) { throw CommonIDLTypeConversor.toIDL(e); } catch (es.udc.fbellas.corbaws.thermostats. ThermostatNotFoundException e) { throw new es.udc.fbellas.corbaws.tcs.idl. MalfunctioningException (e.getMessage()); } } Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::server::wrappers::ThermostatImpl (y 4) public void setNominalTemperature(short value) throws es.udc.fbellas.corbaws.tcs.idl.TemperatureOutOfRangeException, es.udc.fbellas.corbaws.tcs.idl.MalfunctioningException { try { synchronized(this) { driver.setNominalTemperature(identifier, value); nominalTemperature = value; } } catch (es.udc.fbellas.corbaws.thermostats. TemperatureOutOfRangeException e) { throw CommonIDLTypeConversor.toIDL(e); } catch (es.udc.fbellas.corbaws.thermostats. MalfunctioningException e) { throw CommonIDLTypeConversor.toIDL(e); } catch (es.udc.fbellas.corbaws.thermostats. ThermostatNotFoundException e) { throw new es.udc.fbellas.corbaws.tcs.idl. MalfunctioningException(e.getMessage()); } } Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::idlutil::CommonIDLTypeConversor // ... public final static es.udc.fbellas.corbaws.tcs.idl.MalfunctioningException toIDL(es.udc.fbellas.corbaws.thermostats.MalfunctioningException e) { return new es.udc.fbellas.corbaws.tcs.idl.MalfunctioningException( e.getEncapsulatedException().getMessage()); } public final static es.udc.fbellas.corbaws.thermostats.MalfunctioningException fromIDL(es.udc.fbellas.corbaws.tcs.idl.MalfunctioningException e) { return new es.udc.fbellas.corbaws.thermostats.MalfunctioningException( new Exception(e.fExplanation)); } // ... Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::server::wrappers::ControllerImpl (1) class ControllerImpl extends ControllerPOA { /* * The methods of this class need not be synchronized because they are * read-only. */ // ... ControllerImpl(ORB orb) throws IncorrectModelException, es.udc.fbellas.corbaws.thermostats.ThermostatNotFoundException, es.udc.fbellas.corbaws.thermostats.MalfunctioningException { this.orb = orb; Iterator drivers = DriverManager.drivers(); while (drivers.hasNext()) { Driver driver = (Driver)drivers.next(); String model = driver.getModel(); Iterator identifiers = driver.findAllIdentifiers().iterator(); while (identifiers.hasNext()) { String identifier = (String)identifiers.next(); ThermostatImpl thermostatImpl = new ThermostatImpl(identifier, driver); thermostatImpls.put(identifier, thermostatImpl); } } } Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::server::wrappers::ControllerImpl (y 2) public ThermostatListItem findByIdentifier(String identifier) throws es.udc.fbellas.corbaws.tcs.idl.ThermostatNotFoundException, es.udc.fbellas.corbaws.tcs.idl.MalfunctioningException { ThermostatImpl thermostatImpl = (ThermostatImpl)thermostatImpls.get(identifier); if (thermostatImpl == null) { throw new es.udc.fbellas.corbaws.tcs.idl.ThermostatNotFoundException( identifier); } return new ThermostatListItem(thermostatImpl._this(orb), thermostatImpl.getState()); } // ... Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::server::main Server <<static>> + main(args : String[]) : void <<use>> W rappersFacade (from wrappers) Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::proxies (1) Controller ThermostatCollectionItem - controller : es::udc::fbellas::corbaws::tcs::idl::Controller - thermostat : Thermostat - state : ThermostatState initialize(orb : org::omg::CORBA::ORB) <<return>> + findByLocation(location : String) : Collection + findByIdentifier(identifier : String) : ThermostatCollectionItem ThermostatCollectionItem(thermostat : Thermostat, state : ThermostatState) + getThermostat() : Thermostat + getState() : ThermostatState Posee bloque "static" estático "controller". 1 1 para inicializar el atributo <<IDL interface>> ThermostatState Controller (from thermostats) (from idl) <<return>> 1 Thermostat AdminIDLTypeConversor - thermostat : es::udc::fbellas::corbaws::tcs::idl::Thermostat - identifier : String - model : String Thermostat(thermostat : es::udc::fbellas::corbaws::tcs::idl::Thermostat, identifier : String, model : String) Contiene métodos para convertir desde tipos de "es::udc::fbellas::corbaws::tcs::idl" a tipos de "es::udc::fbellas::corbaws::tcs:proxies". + getIdentifier() : String + getModel() : String + getState() : ThermostatState + setNominalTemperature(nominalTemperature : short) : void + setLocation(location : String) : void 1 <<IDL interface>> Thermostat (from idl) Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::proxies (y 2) ProxiesFacade - orb : org::omg::CORBA::ORB + initialize(args : String[]) : void + freeResources() : void <<use>> Controller * initialize: inicializa el ORB y el Controller. * freeResources: destruye el ORB. Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::proxies::Thermostat // ... public es.udc.fbellas.corbaws.thermostats.ThermostatState getState() throws es.udc.fbellas.corbaws.thermostats.MalfunctioningException, es.udc.fbellas.corbaws.util.exceptions.InternalErrorException { try { return CommonIDLTypeConversor.fromIDL(thermostat.getState()); } catch (es.udc.fbellas.corbaws.tcs.idl.MalfunctioningException e) { throw CommonIDLTypeConversor.fromIDL(e); } catch (org.omg.CORBA.SystemException e) { throw AdminIDLTypeConversor.fromIDL(e); } } // ... Diseño por capas – Ejemplo Implementación de es::udc::fbellas::corbaws::tcs::idlutil::CommonIDLTypeConversor // .... public final static es.udc.fbellas.corbaws.thermostats.ThermostatState fromIDL( es.udc.fbellas.corbaws.tcs.idl.ThermostatState state) { return new es.udc.fbellas.corbaws.thermostats.ThermostatState( state.fIdentifier, state.fModel, state.fTemperature, state.fNominalTemperature, state.fLocation); } // ... Diseño por capas – Ejemplo es::udc::fbellas::corbaws::util::shell CommandManager - commands : java::util::Map register(command : Command) : void lookup(commandName : String) : Command commands() : java::util::Iterator run(inputStream : java::io::InputStream, printStream : java::io::PrintStream, prompt : String) : void handles 0..n <<Interface>> Command + getName() : String + getSyntax() : String + getExplanation() : String + execute(printStream : java::io::PrintStream, args : String[]) : boolean CommandTemplate - name : String - syntax : String - explanation : String + CommandTemplate(name : String, syntax : String, explanation : String) + getName() : String + getSyntax() : String + getExplanation() : String + execute(printStream : java::io::PrintStream, args : String[]) : boolean <<abstract>> # syntaxOK(args : String[]) : boolean <<abstract>> # processCommand(printStream : java::io::PrintStream, args : String[]) : boolean # printInvalidSyntax(printStream : java::io::PrintStream) : void HelpCommand ExitCommand HelpCommand() ExitCommand() Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::admin::shell::commands (1) CommandTemplate (from shell) FindByIdentifierCommand ListCommand - thermostats : java::util::Map FindByIdentifierCommand() + ListCommand() FindByLocationCommand FindByLocationCommand() SetLocationCommand SetNominalTemperatureCommand SetLocationCommand() SetNominalTemperatureCommand() <<use>> 0..n Thermostat <<use>> <<use>> <<use>> Controller (from proxies) <<use>> (from proxies) Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::admin::shell::commands (y 2) CommandsFacade + run(inputStream : java::io::InputStream, printStream : java::io::PrintStream) : void * Incluye bloque "static" para instanciar todos los comandos y registrarlos en "es::udc::fbellas::corbaws::util::shell::CommandManager". * El método "run" invoca a "es::udc::fbellas::corbaws::util::shell:: CommandManager.run(inputStream, printStream, "TCSAdmin> ")". Diseño por capas – Ejemplo es::udc::fbellas::corbaws::tcs::admin::shell::main Console <<static>> + main(args : String[]) : void <<use>> ProxiesFacade (from proxies) <<use>> CommandsFacade (from commands) Otras opciones de diseño n Caché en cliente n n n n Caché en el servidor Multithreading n n Normalmente cualquier ORB soporta los modelos: pool de threads, thread por petición y thread por objeto Distribución en varios servidores (“federation”) n n Problemas de consistencia Mejor limitarse a cachear estado de objetos pertenecientes al cliente Ejs.: servicio de nombres, la práctica, etc. Usos avanzados del POA n Patrón “Evictor” n n Patrón “Default servant” n n Permite implementar una caché de objetos CORBA Permite implementar múltiples objetos CORBA con un sólo servant en memoria Más información en http://www.tic.udc.es/~fbellas/teaching/adoo2000-2001