我目前正在进行一个小组项目,以便在Java中重新创建最初的Pokemon黄色版本。我们刚刚开始计划它,但是我们现在已经画出的设计方法是有一个抽象的口袋妖怪类,并且有151个类(原始口袋妖怪的计数)来扩展它,每个类都是不同的口袋妖怪。每个类将能够存储数据,如ID、名称、口袋妖怪的类型(即:rock/water/fire/等等),以及它可以使用和学习的动作。为口袋妖怪创建151个类似乎有点麻烦,但它似乎也是限制耦合的好方法。就设计而言,这是一个很好的方法去做它,如果不是,有什么更好的方法去做呢?
发布于 2013-11-17 11:10:42
您并没有真正解释为什么您的小组认为生成151个类是个好主意(希望如此),因为“减少耦合”还没有真正的意义,因为您没有设计或代码,因此也没有耦合。
每个类将能够存储数据,如ID、名称、口袋妖怪的类型(即:rock/water/fire/等等),以及它可以使用和学习的动作。
将该句子中的“类”改为“实例”,您就会明白为什么只需要创建一个Pokemon类:它们都共享相同的属性。派生类应该添加或实现该派生所特有的属性(和/或方法)。
如果您将ElementType、Move和PokemonBase的属性定义为:
enum ElementType
{
Fire = 1,
Water = 2,
Grass = 4,
Rock = 8,
}
class Move
{
int ID;
string Name;
ElementType ElementType;
int BaseDamage;
}
class PokemonBase
{
int ID;
string Name;
ElementType ElementTypes;
List<Move> Moves;
decimal HitPointsPerLevel;
decimal AttackPointsPerLevel;
}那么,您的各种实例仅在这些属性中的内容不同。您的类和代码不应该关心这个问题。具有Fire类型的口袋妖怪可以与类型为水的口袋妖怪存储在完全相同的类中:它们只具有不同的ElementTypes属性值。
现在,您可以使用它们的属性和移动来填充Pokemon实例的列表,例如,以您喜欢的格式加载文本文件或从数据库读取。
在加载口袋妖怪时,您可以解决所有这些问题。在数据源中,您可以存储其他属性,在“它可以使用和学习的移动”的情况下,这些属性可以是一个关系表(或等效的平面文本),您可以在其中将Pokemon.ID、Move.ID和Level连接到对方(如果内存正确的话,他们将学习达到预设级别的新步骤),将其存储在口袋妖怪类中的列表或字典属性中。
或者,在设计的时候,你认为你需要一个不同的类来为每个精灵实现不同的战斗系统。您不必这样做,因为通过组合各种属性,您可以为每个实例赋予其独特的属性。
当然,当您意识到实际上有两种类型的口袋妖怪时,问题就开始了:一种是游戏中可能出现的所有口袋妖怪的“蓝图”,另一种是玩家携带的一个实际的口袋妖怪,它有一个级别、HitPoints等,所有属性都取决于或通过该级别计算,或者基于“在游戏中”这个事实。可以在派生类中实现这些属性:
class Pokemon : PokemonBase
{
int Level;
int MaxHitPoints;
int HitPoints;
// Stats, for example
int Attack { get { return Level * base.AttackPointsPerLevel } };
int Defense;
// This method directly applies the attack results to the incoming Pokemon, by reference.
int Attack(Pokemon other, Move attack)
{
int damageDone = (this.Attack * attack.BaseDamage) - defender.Defense;
// TODO: http://bulbapedia.bulbagarden.net/wiki/Type_chart
decimal modifier = CalculateEffectiveness(other, attack);
damageDone *= modifier;
other.HitPoints -= damageDone;
return damageDone;
}
}所有Pokemon实例都可以使用它,而无需派生任何内容。关键是对类和方法进行建模,以便您想要对每个类和方法执行的操作可以应用于所有类型的组合。例如,您还可以向List<DamageOverTimeAttack>添加一个Pokemon,以实现应该保持活动几个回合的攻击,并在Attack()方法中处理它。
现在,当玩家遇到一个新的口袋妖怪时,您可能需要从一个工厂创建它:
class PokemonFactory
{
List<PokemonBase> _allPokemon;
Pokemon GetPokemon(id, level)
{
blueprint = _allPokemon.Find(id);
return new Pokemon
{
ID = blueprint.ID,
Name = blueprint.Name,
...
Level = level,
...
};
}
}发布于 2013-11-17 09:46:34
不同的pokemons应该在代码中公开不同的API吗?你应该先分析一下这个。
它们的ID、名称和类型可以存储在成员中。现在你可以说他们有不同的能力。所以每一种能力都应该是一种方法。但你错了。这样做会阻止您在管理战斗时编写简单的代码。
你应该在一个单独的类中抽象出一种能力。它可以用它的成本和效果来定义。那么每个生物都有一个能力列表。您的151个pokemons是在服务方法或配置文件中定义的。在编写特性时,代码不应该关心传递什么样类型的口袋妖怪。
151类建模是一个巨大的复杂成本。很可能,您必须使用反射来检查与某个特定类相关的数据。这将是对java类系统的误用。
发布于 2013-11-17 09:01:06
我相信,如果你有一个基本的口袋妖怪类型,它可能更容易从基类中只派生出火、水、电类型,以限制所需的不同类的数量。由此,您可以设置口袋妖怪类型之间的关系,而不需要太多麻烦,例如。火胜水、草、弱、电等。
从这里,您可能有一个数据库或JSON/XML文件,它存储每个pokemom、ID、name的所有统计数据,以及它是从哪个演化来的。这种格式绝对可以帮助对数据进行分组和分类,而不需要太多开销。一旦程序按顺序读取JSON/XML文件,程序初始化后,每个pokemon类型类都可以提取数据。一旦您在文件存储中设置了所有的状态,那么整个过程就可以自动化了。从那里,访问每个口袋妖怪应该是简单的!
https://softwareengineering.stackexchange.com/questions/218680
复制相似问题