Lets say I'm making a game with a bunch of levels, whereby as you progress through levels, new types of enemies are introduced. If a developer has an enemy and wants to get the level an enemy can be found in, the most intuitive way I can think of allowing them to do this would be to store a list levels on the enemy that they are contained within. This however I feel like is a problem, because now I've coupled my enemy and my level together. My enemy now knows about the context in which it used. My research says that these kinds of relationships are unhealthy for maintaining software, but it seems intuitive to use from a level above.
Here's a very basic implementation:
public class Level
{
List<Enemy> typesOfEnemies;
public Level(List<Enemy> typesOfEnemies)
{
this.typesOfEnemies = new List<Enemy>(typesOfEnemies);
for (int i = 0; i < typesOfEnemies.Count; i++)
{
typesOfEnemies[i].AddLevelContainedWithin(this);
}
}
}
public class Enemy
{
public List<Level> levelsContainedWithin = new List<Level>();
public void AddLevelContainedWithin(Level level)
{
levelsContainedWithin.Add(level);
}
}
So there's probably a few problems associated with this code. The most obvious one that sticks out to me is that I can add a level to an enemy without updating my Level's types of enemies which puts them out of sync. I could resolve this by creating another class that does the work, ensuring the Level and Enemy stay in-sync, but now I've added a layer of code complexity. The developer now has another class they have to know about to work with.
My question is, how do I make this maintainable and intuitive to for a developer to use?