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.
Aqui estão meus casos de uso comuns:
- Eu preciso chamar vários métodos em todos os módulos.
- Preciso obter uma captura instantânea do estado atual de todos os dados em todos os módulos.
- Eu preciso chamar vários métodos em todos os módulos em uma categoria específica (mas não na subcategoria).
- 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ê"
- 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:
- Eu tenho uma classe que contém a
Map<Category, CategoryDataStructure>
. Essa classe também mantém umaCollection<Module>
visão separada dos dados para uso com o requisito nº 2. CategoryDataStructure
possui métodos de delegação em cadeia que enviam a chamada de método pela cadeia, viaSubCategoryDataStructure
.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:
- 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. - Faça tudo em uma estrutura de dados plana e percorra toda a estrutura quando estiver procurando por uma categoria ou subcategoria específica.
fonte
handleVisitor
classe?Respostas:
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.
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:
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:
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:
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
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()
.fonte