对象包含有关使用它们的上下文的信息是否有代码味道?
Is it a code smell for objects to contain information regarding the context in which they're used?
假设我正在制作一款包含多个关卡的游戏,随着关卡的推进,会引入新类型的敌人。如果开发者有一个敌人并且想要获得可以找到敌人的关卡,我能想到的允许他们这样做的最直观的方法是存储一个包含他们的敌人的关卡列表。然而,我觉得这是一个问题,因为现在我已经将我的敌人和我的关卡结合在一起了。我的敌人现在知道它使用的上下文。我的研究表明,这些类型的关系不利于维护软件,但从更高级别使用它似乎很直观。
这是一个非常基本的实现:
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);
}
}
所以这段代码可能存在一些问题。对我来说最明显的一个是我可以在不更新关卡的敌人类型的情况下为敌人添加一个关卡,这会使它们不同步。我可以通过创建另一个 class 来解决这个问题,确保 Level 和 Enemy 保持同步,但现在我增加了一层代码复杂性。开发人员现在有另一个 class 他们必须知道的工作。
我的问题是,如何使开发人员可以维护和直观地使用它?
你可以介绍另一个做耦合的class:
class LevelManager
{
public List<Enemy> GetEnemies(Level l) { ... }
public List<Level> GetLevels(Enemy e) { ... }
}
现在您所有的数据对象都必须知道 LevelManager
。
实际上,在您的 Enemy
- 和 Level
-class 中甚至都不需要这种依赖。如果开发人员想知道敌人可以在哪些级别生成,他可以改用 LevelManager
。
假设我正在制作一款包含多个关卡的游戏,随着关卡的推进,会引入新类型的敌人。如果开发者有一个敌人并且想要获得可以找到敌人的关卡,我能想到的允许他们这样做的最直观的方法是存储一个包含他们的敌人的关卡列表。然而,我觉得这是一个问题,因为现在我已经将我的敌人和我的关卡结合在一起了。我的敌人现在知道它使用的上下文。我的研究表明,这些类型的关系不利于维护软件,但从更高级别使用它似乎很直观。
这是一个非常基本的实现:
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);
}
}
所以这段代码可能存在一些问题。对我来说最明显的一个是我可以在不更新关卡的敌人类型的情况下为敌人添加一个关卡,这会使它们不同步。我可以通过创建另一个 class 来解决这个问题,确保 Level 和 Enemy 保持同步,但现在我增加了一层代码复杂性。开发人员现在有另一个 class 他们必须知道的工作。
我的问题是,如何使开发人员可以维护和直观地使用它?
你可以介绍另一个做耦合的class:
class LevelManager
{
public List<Enemy> GetEnemies(Level l) { ... }
public List<Level> GetLevels(Enemy e) { ... }
}
现在您所有的数据对象都必须知道 LevelManager
。
实际上,在您的 Enemy
- 和 Level
-class 中甚至都不需要这种依赖。如果开发人员想知道敌人可以在哪些级别生成,他可以改用 LevelManager
。