Tengo un montón de módulos. Puedo dividir estos módulos en diferentes categorías que están completas y no se superponen. Por ejemplo, tres categorías, con ids que se pueden expresar como Animal
, Vegetable
, y Mineral
. Además, desgloso estas categorías en subcategorías, que de nuevo son distintas, completas y no se superponen. Por ejemplo, ids que se pueden expresar como Mammal
, Reptile
, Legume
, Root
, Rock
, Gem
. Por último, debajo de estas categorías, no son los propios módulos, por ejemplo Cat
, Dog
, Iguana
, Bean
, Quartz
, Emerald
, etc.
Aquí están mis casos de uso comunes:
- Necesito llamar a varios métodos en todos los módulos.
- Necesito obtener una instantánea plana del estado actual de todos los datos en todos los módulos.
- Necesito llamar a varios métodos en todos los módulos en una categoría particular (pero no en una subcategoría).
- Necesito llamar a varios métodos en un módulo específico en particular en función de su ID conocida.
- Esto podría ser un "hacer algo" o un "dime algunos datos sobre ti"
- Necesito almacenar datos agregados sobre todos los módulos en una categoría particular (pero no en una subcategoría).
¿Cómo debo almacenar estos datos?
Algunos otros hechos relevantes:
- Las categorías se establecen en tiempo de ejecución.
- Como tal, los módulos de nivel inferior comparten una interfaz común.
- Una vez que están configurados, no cambian en esa ejecución en particular, se basan en datos en archivos de configuración.
Esto es lo que hago actualmente:
- Tengo una clase que contiene a
Map<Category, CategoryDataStructure>
. Esta clase también mantiene unaCollection<Module>
vista separada de los datos para usar con el requisito # 2. CategoryDataStructure
ha encadenado métodos de delegación que envían la llamada al método por la cadena, a través deSubCategoryDataStructure
.CategoryDataStructure
También almacena los datos agregados utilizados en el requisito n. ° 5.
Funciona, pero sinceramente es bastante difícil de manejar. Todo es con estado / mutable y difícil de cambiar. Si quiero agregar un nuevo comportamiento, tengo que agregarlo en muchos lugares. Actualmente, las estructuras de datos tienen mucha lógica empresarial también; Los métodos de delegación. Además, la estructura de datos principal tiene que hacer mucha lógica de negocios para crear un módulo en particular y es la estructura de datos principal si es necesario y la estructura de datos de ese padre si es necesario.
Estoy tratando de separar de alguna manera la lógica de administración de datos de la estructura de datos en sí, pero debido al anidamiento es complicado. Aquí hay algunas otras opciones que he estado considerando:
- Cree un simple
Map<Category, Map<Subcategory, Module>>
y coloque todo el código para mantener su estado en una clase diferente. Mi preocupación por hacer esto son los requisitos n. ° 1 y n. ° 2, será difícil mantener la vista consistente, ya que ahora tendré dos estructuras de datos diferentes que representan los mismos datos. - Haga todo en una estructura de datos plana y recorra toda la estructura cuando busque una categoría o subcategoría en particular.
fuente
handleVisitor
clase?Respuestas:
Parece que el problema principal aquí es que tiene objetos organizados en una jerarquía basada en su identidad, pero los está utilizando de manera no jerárquica.
Una analogía sería almacenar archivos en directorios basados en su tipo de archivo, pero buscar a través de cada directorio y solo cargar ciertos basados en algunos criterios distintos del tipo.
Este es un buen objetivo, y hay una manera fácil de comenzar a dividir las responsabilidades sin un refactor importante: usar Visitantes .
La idea es que si necesita inspeccionar u operar solo ciertos elementos de la jerarquía, debe poner esa lógica en el visitante mismo. Luego puede escribir múltiples visitantes, cada uno operando en diferentes elementos y realizando diferentes acciones.
Cada unidad de lógica ahora es autónoma para un visitante específico . Esto mejora la SRP-ness de su código. Si necesita cambiar la forma en que se realiza una operación, solo lo hace en el visitante que implementa esa lógica. Su jerarquía de objetos debe permanecer igual, menos los cambios superficiales para exponer los datos necesarios.
Existen múltiples formas de implementar visitantes según los objetivos específicos, pero la idea general es que cada nodo en la jerarquía acepte un objeto visitante. Su implementación se ve así, y se puede incluir en una clase primaria abstracta:
Esto garantiza que no importa cómo conecte sus nodos en tiempo de ejecución, cada nodo será procesado.
Su visitante se ve así:
El
accept(Node)
método del visitante es donde se realiza el trabajo real. Tendrá que inspeccionar el nodo y condicionalmente hacer cosas diferentes (incluido ignorar el nodo).Por ejemplo, puede hacer lo siguiente:
Cada visitante es una clase autónoma que contiene la lógica para cada operación sin necesidad de mezclar preocupaciones con otros visitantes o los nodos.
fuente
Un nivel profundo de anidamiento sugiere que debería refactorizar las acciones en funciones más pequeñas, que pueden encadenarse mediante declaraciones de retorno. Si necesita aplicar el mismo método para múltiples entradas, podría aprovecharlo
function.apply()
en Java 8.Suponiendo que los diferentes elementos no comparten ninguna propiedad, podría implementar una interfaz , que requiere que se implemente un conjunto específico de métodos. Para cada nivel me gustaría crear una interfaz, que luego se extendió para cada sub-nodo, por ejemplo:
Entity
,Phylum
,Species
. Además, necesita tres clases para cada una de las tres entidades.Los datos se pueden almacenar como propiedades de las instancias de objeto. Para lograr una instantánea plana, iteraría a través de los datos usando
function.apply()
.fuente