I have a bunch of modules. I can break these modules into different categories that are complete and do not overlap. For example, three categories, with ids that can be expressed as Animal, Vegetable, and Mineral. I further break these categories down into subcategories, which again are distinct, complete and do not overlap. For example, ids that can be expressed as Mammal, Reptile, Legume, Root, Rock, Gem. Finally, underneath these categories, there are the modules themselves, e.g. Cat, Dog, Iguana, Bean, Quartz, Emerald, etc.Here are my common use cases:I need to call various methods on all modules.I need to get flat snapshot of the current state of all data across all modules.I need to call various methods on all modules in a particular category (but not subcategory).I need to call various methods on a particular specific module based on it's known ID.This could be a do something or a tell me some data about yourselfI need to store aggregate data about all modules in a particular category (but not subcategory).How should I store this data?Some other relevant facts:The categories are established in runtimeAs such, the bottom level modules do share a common interface.Once they're set up, they don't change in that particular run - they are based on data in configuration files.Here's what I currently do:I have a class that contains a Map<Category, CategoryDataStructure>. This class also maintains a separate Collection<Module> view of the data for use with requirement #2. CategoryDataStructure has chained delegation methods that send the method call down the chain, via SubCategoryDataStructure.CategoryDataStructure also stores the aggregate data used in requirement #5.It works, but it's honestly quite unwieldy. The whole thing is stateful / mutable and hard to change. If I want to add new behavior I have to add it in many places. Currently the data structures themselves have a lot of business logic as well; the delegation methods. Also the parent data structure has to do a lot of business logic to create a particular module and it's parent data structure if need and that parent's data structure if needed.I'm looking to somehow break apart data management logic away from the data structure itself, but because of the nesting it's complicated. Here are some other options I've been considering:Create a simple Map<Category, Map<Subcategory, Module>>, and put all the code to keep its state in a different class. My concern with doing this is requirements #1 and #2, it will be difficult to keep the view consistent as now I'll have two different data structures that represent the same data.Do everything in a flat data structure, and loop through the entire structure when I'm looking for a particular category or sub category.
How do I store multiple views of data in memory?
java;design;object oriented design;data structures
It appears that the core issue here is you have objects arranged in a hierarchy based on their identity, but are using them in a non-hierarchical manner.An analogy would be storing files in directories based on their file type, but searching through each directory and only loading certain ones based on some criteria other than the type.I'm looking to somehow break apart data management logic away from the  data structure itself, but because of the nesting it's complicated.  Here are some other options I've been considering:This is a good goal, and there is an easy way to start splitting the responsibilities without a major refactor: use Visitors.The idea is that if you need to inspect or operate on only certain elements in the hierarchy, you put that logic in the visitor itself. You can then write multiple visitors, each one operating on different elements, and performing different actions.Each unit of logic is now self-contained to a specific visitor. This enhances the SRP-ness of your code. If you need to change how an operation is done, you do so only in the visitor that implements that logic. Your object hierarchy should stay the same, minus superficial changes to expose necessary data.There are multiple ways to implement visitors depending on the specific goals, but the general idea is each node in the hierarchy accepts a visitor object. Its implementation looks like this, and can be stuffed into some abstract parent class:public class Node {  public void accept(Visitor v) {    v.accept(this);    for (Node child : children) {      child.accept(v);    }  }}This ensures that no matter how you wire up your nodes at runtime, every node will be processed.Your visitor looks like this:public interface Visitor {  accept(Node n);}The visitor's accept(Node) method is where the real work is done. You will need to inspect the node and conditionally do different things (including ignore the node).For example, you might be able to do the following: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());Each visitor is a self-contained class that contains the logic for each operation with no need to mix concerns with other visitors or the nodes.