See: Description
Interface | Description |
---|---|
Locator |
Manages references to the objects of a Library or module.
|
Locator_withExists |
Add method exists to the Locator API
This interface is to maintain compatility with the interface Locator.
|
LocatorObjectFactory |
Interface for factories of objects to be registered in a Locator.
|
Class | Description |
---|---|
AbstractLocator |
Locator implementation based on the use of the ExtensionPoints.
|
BaseLocator |
Base Locator implementation using the Java class name as the Locator name.
|
Exception | Description |
---|---|
LocatorException |
Exception for errors related to the retrieval of references through a
Locator.
|
LocatorReferenceException |
Exception for errors related to the retrieval of references through a
Locator.
|
ReferenceNotRegisteredException |
Exception for errors related to the initialization of a Library.
|
Proporciona una implementación base del patrón Locator, así como un mecanismo de inicialización que permita registrar las implementaciones que aporte en su Locator cada módulo o librería.
Se ha creado un interfaz Locator
, que define
los métodos de uso general del Locator, así como una implementación abstracta,
AbstractLocator
basada en el uso de los puntos
de extensión:
El interfaz del Locator se han definido métodos que permiten registrar clases, o factorías, así cómo obtener instancias de los mismos. Hay que tener en cuenta que el comportamiento del Locator no es como factoría, es decir, se espera que el método get devuelva cada vez la misma instancia de objeto. Es decir, actúa como un contenedor de referencias a objetos que funcionan como Singleton.
La implementación abstracta la aporta la clase
AbstractLocator
.
La idea es que cada librería implemente su Locator extendiendo
esta clase, y aportando métodos que registren y obtengan referencias a
sus tipos de objetos directamente. Más adelante, se muestra un ejemplo
de implementación que aporta este tipo de métodos. Además, cada implementación
de Locator deberá hacerse empleando el patrón Singleton,
para facilitar el acceso al Locator.
Las librerías de gvSIG deberán usar el Locator en los siguientes casos:
Con el mecanismo de los Locator queda un tema pendiente por resolver: cúando se realiza el registro e inicialización de los objetos. Dado que podemos tener una serie de relaciones de dependencia entre distintas librerías que empleen el mecanismo de los Locator, así como éstas con las Extensiones, es necesario que se registren todas las implementaciones antes de que se usen dentro de la aplicación.
Para ello se ha definido el Library. Los objetos de este tipo se encargarán de registrar las implementaciones disponibles a través de los Locator, así como de validar que, para una librería de tipo API, se haya registrado alguna implementación.
Se define un interfaz org.gvsig.tools.locator.Library
con dos
métodos que permitan realizar las acciones planteadas anteriormente
Se ha empleado la misma nomenclatura que en las Extensiones de gvSIG, ya que cada uno de los métodos de un Library se invocará desde una Extensión, en el método correspondiente. Es decir, primero se invocan todos los métodos initialize() de las extensiones, y desde estos los de las librerías, y luego los métodos postInitialize()
Cada librería que emplee el mecanismo del Locator deberá implementar un Library, y aportar una extensión de gvSIG que se encargue de invocarla. Distinguiremos dos tipos de librería (aunque pueden coincidir en algunos casos con sublibrerías): librerías de API y librerías de implementación.
En ambos casos, los métodos del Library también podrán ser usados para realizar otros tipos de inicializaciones, como definir variables de librería, cargar configuraciones, obtener recursos, etc.
Se ha creado una clase org.gvsig.locator.BaseLibrary que facilita la implementación, al tener implementados ya los métodos, aunque sólo para hacer logging. Si la librería es de tipo API o implementación, bastará con implementar uno de los dos métodos.
A continuación se plantea un ejemplo, basado en la librería libCompat. Esta librería plantea el caso de implementaciones alternativas para Java SE y Java ME, por lo que requerirá una estructura de proyectos que permita organizar la librería en tres componentes de instalación:
A continuación se muestran ejemplos de código, centrándonos en el uso de una de las utilidades de libCompat: StringUtils.
Primero, en libCompat se incluirá la definición del interfaz de StringUtils:
package org.gvsig.compat.lang;
public interface StringUtils {
String[] split(String input, String regex);
...
}
En libCompatSE, tendremos una implementación:
package org.gvsig.compat.se.lang;
import org.gvsig.compat.lang.StringUtils;
public class StandardStringUtils implements StringUtils {
public String[] split(String input, String regex) {
return input == null ? null : input.split(regex);
}
...
}
En libCompatME, la implementación alternativa:
package org.gvsig.compat.me.lang;
import java.util.regex.Pattern;
import org.gvsig.compat.lang.StringUtils;
public class MobileStringUtils implements StringUtils {
public String[] split(String input, String regex) {
return input == null ? null : Pattern.compile(regex).split(input, 0);
}
...
}
Con este esquema, las implementaciones no incorporan nada en lo que respecta a cómo se crean o se obtienen, lo que mejora su extensibilidad y la creación de tests unitarios. Además, se facilita la programación con interfaces y la definición de APIs.
En libCompat tenemos la implementación del Locator de la librería, que quedaría como sigue:
package org.gvsig.compat;
import org.gvsig.compat.lang.StringUtils;
public class CompatLocator extends AbstractLocator {
/** The name of the StringUtils reference. */
public static final String STRINGUTILS_NAME = "StringUtils";
private static final String LOCATOR_NAME = "CompatLocator";
/** Unique instance. */
private static final CompatLocator instance = new CompatLocator();
/**
* Return the singleton instance.
* @return the singleton instance
*/
public static CompatLocator getInstance() {
return instance;
}
/**
* Return a reference to StringUtils.
* @return a reference to StringUtils
* @throws LocatorException
* if there is no access to the class or the class cannot be
* instantiated
* @see Locator#get(String)
*/
public static StringUtils getStringUtils() throws LocatorException {
return (StringUtils) getInstance().get(STRINGUTILS_NAME);
}
/**
* Registers the Class implementing the StringUtils interface.
* @param clazz
* implementing the StringUtils interface
*/
public static void registerStringUtils(Class clazz) {
getInstance()
.register(STRINGUTILS_NAME, STRINGUTILS_DESCRIPTION, clazz);
}
protected String getLocatorName() {
return LOCATOR_NAME;
}
}
En esta implementación de Locator, se han añadido métodos estáticos que facilitan el uso del mismo, y manejan referencias de los tipos de la librería en concreto, como el StringUtils. Así, por ejemplo, los clientes no tienen porqué usar el método get(name) genérico del Locator, sino que pueden usar un método getStringUtils() directamente.
Una clase cliente que quiera usar, por ejemplo, el StringUtils, podrá hacerlo de la siguiente forma:
public class SampleClient {
private StringUtils stringUtils = CompatLocator.getStringUtils();
public void doSomething() {
String text = "one,two,three";
String[] words = stringUtils.split(text, ",");
...
}
}
En cuanto a las librerías, libCompatSE y libCompatME tendrán cada una su Library que registre la implementación correspondiente de StringUtils.
SECompatLibrary:
package org.gvsig.compat.se;
import org.gvsig.compat.CompatLocator;
/**
* Initialization of the libCompat library, Java Standard Edition
* implementation.
* @author <a href="mailto:cordin@disid.com">Cèsar Ordiñana</a>
*/
public class SECompatLibrary extends BaseLibrary {
public void initialize() {
super.initialize();
CompatLocator.registerStringUtils(StandardStringUtils.class);
}
}
MECompatLibrary:
package org.gvsig.compat.me;
import org.gvsig.compat.CompatLocator;
/**
* Initialization of the libCompat library, Java Mobile Edition implementation.
* @author <a href="mailto:cordin@disid.com">Cèsar Ordiñana</a>
*/
public class MECompatLibrary extends BaseLibrary {
public void initialize() {
super.initialize();
CompatLocator.registerStringUtils(MobileStringUtils.class);
}
}
La librería de API libCompat, tiene un Library que comprueba si se ha registrado alguna de las implementaciones:
CompatLibrary:
package org.gvsig.compat;
import org.gvsig.compat.lang.StringUtils;
/**
* Initialization of the libCompat library.
* @author <a href="mailto:cordin@disid.com">Cèsar Ordiñana</a>
*/
public class CompatLibrary extends BaseLibrary {
public void postInitialize() {
super.postInitialize();
// Validate there is any implementation registered.
StringUtils stringUtils = CompatLocator.getStringUtils();
if (stringUtils == null) {
throw new ReferenceNotRegisteredException(CompatLocator.STRINGUTILS_NAME,
CompatLocator.getInstance());
}
}
}
Dado que con el uso de los Locator estamos reforzando el uso de interfaces, y la separación de implementaciones alternativas u opcionales, podemos aprovechar esto a la hora de realizar los tests unitarios.
La forma de aprovecharlo consiste en crear los tests unitarios que comprueben el funcionamiento correcto a nivel de API, dentro del proyecto que incluya la definición de los interfaces. Dichos tests pueden crearse como clases abstractas, que definan métodos para crear los objetos que implementan los interfaces a probar. Así, cada implementación particular podrá crear sus tests unitarios, símplemente heredando de los tests del API, e implementando el método de creación de sus clases propias.
Con esta estructura de tests unitarios podemos definir los tests unitarios a nivel de API, y lanzarlos de forma sencilla para cada implementación.
Basándonos en el ejemplo anterior, la distribución de tests unitarios quedaría de la siguiente forma en las tres librerías del ejemplo:
libCompat:
public abstract class StringUtilsTestAbstract extends TestCase {
private StringUtils utils;
protected void setUp() throws Exception {
super.setUp();
utils = createUtils();
}
protected abstract StringUtils createUtils();
public void testReplaceAll() {
String testString = "En un lugar de la Mancha";
String resultString = "En_un_lugar_de_la_Mancha";
String regex = " ";
String replacement = "_";
assertEquals(resultString,
utils.replaceAll(testString, regex, replacement));
}
}
libCompatSE:
public class StandardStringUtilsTest extends StringUtilsTestAbstract {
protected StringUtils createUtils() {
return new StandardStringUtils();
}
}
libCompatME:
public class MobileStringUtilsTest extends StringUtilsTestAbstract {
protected StringUtils createUtils() {
return new MobileStringUtils();
}
}
Las excepciones del Locator y del Library son de tipo RuntimeException por lo que, aunque declaradas en su API, no obligan a ser capturadas mediante un try ... catch. A nivel de aplicación, si que habrá que hacer un tratamiento de estas excepciones, para mostrar al usuario algún tipo de información sobre el error producido.
No se han definido excepciones normales, ya que son errores graves y no se puedan tratar desde código. Así no obligamos a capturarlas en cada lugar dónde se use un Locator o Library, y se pueden tratar en un nivel superior.