¿Cómo almaceno múltiples vistas de datos en la memoria?

8

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.

Jerarquía de categoría

Aquí están mis casos de uso comunes:

  1. Necesito llamar a varios métodos en todos los módulos.
  2. Necesito obtener una instantánea plana del estado actual de todos los datos en todos los módulos.
  3. Necesito llamar a varios métodos en todos los módulos en una categoría particular (pero no en una subcategoría).
  4. 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"
  5. 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:

  1. Tengo una clase que contiene a Map<Category, CategoryDataStructure>. Esta clase también mantiene una Collection<Module> vista separada de los datos para usar con el requisito # 2.
  2. CategoryDataStructureha encadenado métodos de delegación que envían la llamada al método por la cadena, a través de SubCategoryDataStructure.
  3. 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:

  1. 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.
  2. Haga todo en una estructura de datos plana y recorra toda la estructura cuando busque una categoría o subcategoría en particular.
durron597
fuente
¿Ha considerado romper la responsabilidad de "averiguar qué objetos en la jerarquía inspeccionar o usar" utilizando uno o más Visitantes?
@Snowman No he considerado esa opción; ¿Está sugiriendo almacenar los datos planos y luego, cuando necesito llamar a un método, enviar al visitante a todos y verificar si algo debe suceder en la handleVisitorclase?
durron597
Es un poco más complicado que eso, solo quería asegurarme antes de escribir una respuesta completa.
Tal vez podría voltear la estructura de arriba hacia abajo y proceder de abajo hacia arriba: thing.getType () devuelve "Animal" Eso es bastante plano.
noumenal

Respuestas:

6

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.


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:

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:

public class Node {
  public void accept(Visitor v) {
    v.accept(this);
    for (Node child : children) {
      child.accept(v);
    }
  }
}

Esto garantiza que no importa cómo conecte sus nodos en tiempo de ejecución, cada nodo será procesado.

Su visitante se ve así:

public interface Visitor {
  accept(Node n);
}

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:

Node root = ...;

// Print information on the hierarchy.
root.accept(new DebugVisitor());

// Does stuff with modules, ignores subcategories.
root.accept(new FrobnicateModulesVisitor());

// Eats vegetables, ignores animals and minerals.
root.accept(new EatYourVegetableVisitor());

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
pregunta rápida con respecto a su clase de Nota: ¿dónde se define "hijos" ... o fue este un ejemplo puramente ilustrativo?
@Bey nada está definido, eso fue puramente ilustrativo. Asumo que los lectores estarán familiarizados con la teoría de grafos y los árboles.
1

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().

noumenal
fuente
Hola, gracias por su respuesta. Los elementos de nivel inferior hacen compartir una interfaz común, pensé que sería claro cuando dije que son generados en tiempo de ejecución, pero yo he editado añadir una frase para aclarar ese punto.
durron597
Me parece que necesita una interfaz también para el siguiente nivel, pero podría estar equivocado.
noumenal