Como guardo várias visualizações de dados na memória?

8

Eu tenho um monte de módulos. Eu posso dividir esses módulos em diferentes categorias que estão completas e não se sobrepõem. Por exemplo, três categorias, com ids que podem ser expressos como Animal, Vegetable, e Mineral. Além disso, divido essas categorias em subcategorias, que novamente são distintas, completas e não se sobrepõem. Por exemplo, identificações que podem ser expressos como Mammal, Reptile, Legume, Root, Rock, Gem. Finalmente, debaixo destas categorias, há os próprios módulos, por exemplo Cat, Dog, Iguana, Bean, Quartz, Emerald, etc.

Hierarquia de categoria

Aqui estão meus casos de uso comuns:

  1. Eu preciso chamar vários métodos em todos os módulos.
  2. Preciso obter uma captura instantânea do estado atual de todos os dados em todos os módulos.
  3. Eu preciso chamar vários métodos em todos os módulos em uma categoria específica (mas não na subcategoria).
  4. Eu preciso chamar vários métodos em um módulo específico específico com base em seu ID conhecido.
    • Pode ser "faça alguma coisa" ou "me conte alguns dados sobre você"
  5. Preciso armazenar dados agregados sobre todos os módulos em uma categoria específica (mas não na subcategoria).

Como devo armazenar esses dados?

Alguns outros fatos relevantes:

  • As categorias são estabelecidas em tempo de execução
    • Como tal, os módulos de nível inferior compartilham uma interface comum.
  • Depois de configurados, eles não mudam nessa execução específica - eles são baseados em dados nos arquivos de configuração.

Aqui está o que eu faço atualmente:

  1. Eu tenho uma classe que contém a Map<Category, CategoryDataStructure>. Essa classe também mantém uma Collection<Module> visão separada dos dados para uso com o requisito nº 2.
  2. CategoryDataStructurepossui métodos de delegação em cadeia que enviam a chamada de método pela cadeia, via SubCategoryDataStructure.
  3. CategoryDataStructure também armazena os dados agregados usados ​​no requisito nº 5.

Funciona, mas é honestamente bastante pesado. A coisa toda é estável / mutável e difícil de mudar. Se eu quiser adicionar um novo comportamento, tenho que adicioná-lo em muitos lugares. Atualmente, as próprias estruturas de dados também possuem muita lógica de negócios; os métodos de delegação. Além disso, a estrutura de dados pai precisa criar muita lógica de negócios para criar um módulo específico, e é a estrutura de dados pai, se necessário, e a estrutura de dados do pai, se necessário.

Estou procurando, de alguma forma, separar a lógica de gerenciamento de dados da própria estrutura de dados, mas, devido ao aninhamento, é complicado. Aqui estão algumas outras opções que estive considerando:

  1. Crie um simples Map<Category, Map<Subcategory, Module>>e coloque todo o código para manter seu estado em uma classe diferente. Minha preocupação em fazer isso é os requisitos 1 e 2, será difícil manter a visualização consistente, pois agora terei duas estruturas de dados diferentes que representam os mesmos dados.
  2. Faça tudo em uma estrutura de dados plana e percorra toda a estrutura quando estiver procurando por uma categoria ou subcategoria específica.
durron597
fonte
Você já pensou em dividir a responsabilidade de "descobrir quais objetos da hierarquia inspecionar ou usar" usando um ou mais visitantes?
@ Boneco de neve eu não considerei essa opção; você está sugerindo armazenar os dados de forma plana e, quando precisar chamar um método, envie o visitante a todos e verifique se algo precisa acontecer na handleVisitorclasse?
precisa saber é o seguinte
É um pouco mais complicado que isso, eu só queria ter certeza antes de digitar uma resposta inteira.
Talvez você possa inverter a estrutura de cima para baixo e prosseguir de baixo para cima: thing.getType () retorna "Animal".
Nummenal

Respostas:

6

Parece que o principal problema aqui é que você tem objetos organizados em uma hierarquia com base em sua identidade, mas os está usando de maneira não hierárquica.

Uma analogia seria armazenar arquivos em diretórios com base em seu tipo de arquivo, mas pesquisar em cada diretório e carregar apenas determinados com base em alguns critérios diferentes do tipo.


Estou procurando, de alguma forma, separar a lógica de gerenciamento de dados da própria estrutura de dados, mas, devido ao aninhamento, é complicado. Aqui estão algumas outras opções que estive considerando:

Esse é um bom objetivo e existe uma maneira fácil de começar a dividir as responsabilidades sem um grande refator: use os Visitantes .

A ideia é que, se você precisar inspecionar ou operar apenas determinados elementos da hierarquia, coloque essa lógica no próprio visitante. Você pode gravar vários visitantes, cada um operando em diferentes elementos e executando ações diferentes.

Cada unidade lógica agora é independente para um visitante específico . Isso aprimora o SRP-ness do seu código. Se você precisar alterar a maneira como uma operação é realizada, faça-o apenas no visitante que implementa essa lógica. Sua hierarquia de objetos deve permanecer a mesma, menos alterações superficiais para expor os dados necessários.

Existem várias maneiras de implementar visitantes, dependendo dos objetivos específicos, mas a ideia geral é que cada nó na hierarquia aceita um objeto de visitante. Sua implementação se parece com isso e pode ser inserida em alguma classe pai abstrata:

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

Isso garante que, independentemente de como você conecte seus nós no tempo de execução, todos os nós serão processados.

Seu visitante fica assim:

public interface Visitor {
  accept(Node n);
}

O accept(Node)método do visitante é onde o trabalho real é feito. Você precisará inspecionar o nó e, condicionalmente, fazer coisas diferentes (incluindo ignorar o nó).


Por exemplo, você pode fazer o seguinte:

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 é uma classe independente que contém a lógica de cada operação, sem a necessidade de misturar preocupações com outros visitantes ou nós.


fonte
pergunta rápida sobre sua classe de nota: onde são definidos "filhos" ... ou esse foi um exemplo meramente ilustrativo?
@Bey nada está definido, isso foi meramente ilustrativo. Presumo que os leitores estejam familiarizados com a teoria dos grafos e as árvores.
1

Um nível profundo de aninhamento sugere que você deve refatorar ações em funções menores, que podem ser encadeadas usando instruções de retorno. Se você precisar aplicar o mesmo método para várias entradas, poderá aproveitar o function.apply()Java 8.

Supondo que os diferentes itens não compartilhem nenhuma propriedade, você pode implementar uma interface , que requer a implementação de um conjunto específico de métodos. Para cada nível eu criaria uma interface, que é então estendido para cada sub-nó, por exemplo: Entity, Phylum, Species. Além disso, você precisa de três classes para cada uma das três entidades.

Os dados podem ser armazenados como propriedades das instâncias do objeto. Para obter um instantâneo plano, eu iteraria nos dados usando function.apply().

noumenal
fonte
Oi, obrigado pela sua resposta. Os elementos de nível mais baixo fazer compartilham uma interface comum, pensei que seria claro quando eu disse que eles são todos gerados em tempo de execução, mas eu editei para adicionar uma frase para esclarecer esse ponto.
precisa saber é o seguinte
Parece-me que você precisa de uma interface também para o próximo nível, mas eu posso estar errado.
Noumenal