2009-02-26 26 views
8

Tengo una clase que gestiona las preferencias del usuario para un gran proyecto de software. Cualquier clase en el proyecto que pueda necesitar establecer o recuperar una preferencia del usuario de una tienda persistente es llamar a los métodos estáticos en esta clase. Esta gestión centralizada permite que las preferencias se eliminen por completo mediante programación, lo que sería imposible si cada pref se manejara cerca de su código de uso, rociado a lo largo del proyecto.¿Es una buena idea una clase de "preferencias maestras"?

Me encontré con otra implicación del diseño de centralización en el curso de esto. El software tiene una API pública. Esa API se puede proporcionar sola en un jar. Las clases en esa API pueden referirse a la clase de gestión de pref. Por lo tanto, el administrador de pref debe ir en el jar API.

Cada preferencia puede tener un valor predeterminado. Tras el inicio del software, ese valor predeterminado puede ser calculado. El algoritmo depende de la preferencia y, por lo tanto, tiende a residir cerca del código de uso. Entonces, si el administrador de preferencias necesita encontrar un valor predeterminado, llama a la clase en cuestión.

Pero ahora ese administrador pref se ha convertido en una "clase de pulpo", absorbiendo todo tipo de clases en el contenedor API que no deberían estar allí. Si no es así, los programas que usan el jar API rápidamente se ejecutan en excepciones de ClassDef. Si lo hace, entonces el contenedor API está ahora hinchado, ya que cada una de esas otras clases puede referirse a otras más.

En general, ¿otros programadores de Java administran sus preferencias con una clase centralizada?

¿Tiene sentido distribuir esa clase de gestión de preferencias estáticas como parte de una API pública?

¿Debería ese administrador pref el guardián del código para determinar los valores predeterminados?

Respuesta

11

En mi humilde opinión, creo que la respuesta a su primera pregunta es "sí" y "no".

Las preferencias se manejan comúnmente como una clase centralizada, en el sentido de que la clase es un "sumidero" para muchas clases en el proyecto. Tratar de hacerlo más cerca del código de llamada significa que si la misma preferencia es más tarde útil en otro lugar, estás en problemas. En mi experiencia, tratar de poner las preferencias "demasiado cerca" también resulta en un manejo muy inconsistente.

Dicho esto, a menudo es preferible utilizar múltiples clases de preferencia o "conjunto de preferencias", cada uno de los cuales soporta un módulo o submódulo. Si observa los componentes principales de su arquitectura, a menudo encontrará que el conjunto de preferencias puede dividirse lógicamente. Esto reduce el desorden en cada clase de preferencia. Más importante aún, le permitirá dividir su programa en varios contenedores en el futuro. Ahora, las calculadoras de "valor predeterminado" se pueden colocar en el módulo pero aún en un área suficientemente global.

También sugeriría no establecer preferencias directamente como métodos estáticos, sino más bien usar alguna operación getInstance() como para obtener una instancia compartida de la administración de preferencias, y luego operar en ella. Dependiendo de su semántica, es posible que desee bloquear ese objeto por un tiempo (por ejemplo, mientras el usuario está editando preferencias en una IU) y esto es más fácil si tiene un objeto real.

Para sus otras preguntas, yo diría que su API pública debería permitir a los usuarios cambiar sus preferencias, pero solo si puede documentar suficientemente cuáles podrían ser los resultados de estos cambios.

Si utiliza una única función de API para obtener el "administrador de referencia", puede ofrecer a los usuarios la posibilidad de proporcionar su propia "calculadora de valores predeterminados". El administrador de preferencias le preguntará a esta calculadora antes de recurrir a la que usted proporcionó de forma predeterminada.

+0

Wow; gran respuesta. Tengo algunas pequeñas objeciones con un 10%, pero la partición de las preferencias relacionadas fue una buena decisión, y una que debería haber pensado. –

+0

A menudo me enfrento al mismo problema (aunque afortunadamente Eclipse no me da muchas opciones), me interesaría escuchar tus argucias :) – Uri

+0

En cuanto a obtener una instancia de administrador compartido, no estaba realmente preocupado por ningún bloqueo excepto en el nivel de escritura del disco (que maneja Java), pero por supuesto hay un nivel más alto de abstracción, me doy cuenta de que también debería preocuparme. –

4

¿No puede manejar las preferencias de una manera realmente genérica? Luego, simplemente usa el administrador de preferencias para manejar la persistencia. Por lo tanto, desde una clase solo le diría al administrador de preferencias PreferenceManager.setPreference (clave, valor) y no le importa lo que está guardando en términos de la semántica de los datos.

¿O estoy simplificando esto demasiado?

+0

Los problemas aparecen cuando desea hacer cosas como borrar todas las preferencias, por ejemplo, cuando prueba el comportamiento de una instalación nueva. Entonces todas las preferencias que varios desarrolladores estaban usando a lo largo de la base del código se vuelven difíciles de rastrear. Y ese es solo un problema. –

1

Es posible que desee buscar en la clase NSUserDefaults de Cocoa como fuente de inspiración. Maneja el problema que describes al tener varias capas de preferencias, llamadas dominios. Cuando busca el valor de una clave, como "PrintUsingAllCaps", primero busca un valor en el dominio local del usuario. Si no se encuentra allí, puede verificar el dominio de todo el sistema o un dominio de nivel de red, y así sucesivamente.

El último lugar absoluto que verifica se llama "Dominio de registro", que es básicamente donde se supone que irán los valores predeterminados codificados. Entonces, en cualquier punto de mi código, puedo escribir una preferencia en el dominio de registro, y NSUserDefaults solo servirá ese valor si el usuario no lo ha anulado.

Por lo tanto, en su caso, podría proporcionar un método para que las clases establezcan un valor predeterminado para una clave antes de que acceda al valor (posiblemente) definido por el usuario. La clase de preferencias no tiene que saber nada sobre las clases a las que sirve.

Como alguien más sugirió, si necesita algo más sofisticado, podría establecer un objeto de devolución de llamada DefaultValueProvider en lugar de un valor recto.

2

No soy Java Dev, pero en lo que respecta a toda la clase de "octopus", ¿no puede simplemente proporcionar una interfaz para el jar y conectar las llamadas entre la interfaz y el administrador de preferencias en tiempo de ejecución, utilizando archivo de configuración de la aplicación para determinar el administrador de preferencias para instanciar?

¿Algo como el patrón de proveedor .NET?

De esta forma puede desacoplar su jar del administrador de preferencias.

0

Eliminé mi primera respuesta ya que entendí mal lo que el autor estaba preguntando.

Para abordar realmente la pregunta real, parece que su deseo de colocar preferencias (y el cálculo de los valores predeterminados) con el código que las usa tiene sentido.

¿Podría cumplir ambos requisitos utilizando una clase de contenedor de preferencias para cada área que sigue un patrón para esa área, pero teniendo que registrarse con una colección de objetos de preferencia "Global"?

Su colección global podría hacer cosas como iterar sobre cada conjunto de preferencias y restablecerlas a sus valores predeterminados, pero sus preferencias se definirían y mantendrían localmente para que no se propaguen a otras partes del código.

El único problema que puedo ver es que si permite que el objeto de preferencia se registre cuando se crea una instancia, corre el riesgo de intentar "restablecer los valores predeterminados" con algunas de las preferencias aún no creadas.

Supongo que esto podría solucionarse haciendo que la clase de "preferencia" principal cree una instancia de todas las demás, luego cualquier fragmento de código podría recuperar su objeto de preferencia local desde el central a través de un captador estático.

Esto parece ser muy parecido a cómo funcionan algunos registradores.Existe un mecanismo central para mantener niveles de registro, flujos de salida y demás, pero cada clase tiene su propia instancia de un método de "registro" y se registra en él.

Espero que esto sea más en el objetivo. Ah, también estoy de acuerdo con la respuesta aceptada, nunca hagas que todos tus métodos sean públicos, siempre usa un getter; estarás feliz de haberlo hecho algún día.

0

La API JSR-10 (java.util.prefs.*) utiliza un método de fábrica con un parámetro Class<?> para crear instancias Preferences. De esta forma, la API puede almacenar preferencias de diferentes clases que pertenecen al mismo paquete en un solo archivo.

Cuestiones relacionadas