Java 中的类型对象模式:通过动态类定义增强灵活性
也称为
- 类型描述符
- 类型安全的枚举
类型对象设计模式的意图
允许创建灵活且可扩展的相关类型集合。
类型对象模式的详细说明以及现实世界中的示例
现实世界中的例子
在角色扮演游戏 (RPG) 角色定制系统中可以看到类型对象模式的类似现实世界例子。在这样的游戏中,玩家可以选择各种角色职业,例如战士、法师和弓箭手,每个角色职业都拥有独特的技能和属性。类型对象模式允许游戏动态定义这些角色职业及其行为。游戏不会对每个职业的细节进行硬编码,而是使用灵活的系统,可以在不更改底层游戏逻辑的情况下添加新的角色类型或修改现有的角色类型。这种可扩展性使开发人员能够通过更新或扩展引入新的角色职业,使游戏保持新鲜感和对玩家的吸引力。
通俗地说
探索 Java 类型对象模式如何实现相关类别的灵活创建和管理,从而实现灵活且可扩展的类别集合,非常适合寻求模块化而不修改现有代码库的 Java 开发人员。
定义一个类型对象类和一个类型化对象类。每个类型对象实例代表不同的逻辑类型。每个类型化对象存储一个指向描述其类型的类型对象的引用。
Java 中类型对象模式的编程示例
类型对象模式是一种设计模式,它允许通过创建一个带有表示对象“类型”的字段的类来创建灵活且可重复使用的对象。这种设计模式对于在 Java 中需要提前定义未定义的类型或需要修改或添加类型的场景非常有价值,从而确保有效的 Java 开发,无需频繁重新编译。
在提供的代码中,类型对象模式在一个迷你糖果粉碎游戏中实现。游戏中有许多不同的糖果,随着游戏的升级,这些糖果可能会发生变化。
让我们分解该实现的关键部分
- **糖果类**:该类代表此模式中的“类型”对象。每个
糖果
都有一个名称
、父级
、积分
和类型
。类型
是一个枚举,可以是CRUSHABLE_CANDY
或REWARD_FRUIT
。
class Candy {
String name;
Candy parent;
String parentName;
int points;
Type type;
Candy(String name, String parentName, Type type, int points) {
// constructor implementation
}
int getPoints() {
// implementation
}
Type getType() {
// implementation
}
void setPoints(int a) {
// implementation
}
}
- **JsonParser 类**:该类负责解析包含糖果详细信息的 JSON 文件。它为 JSON 文件中的每个糖果创建一个
糖果
对象,并将它们存储在哈希表
中。
public class JsonParser {
Hashtable<String, Candy> candies;
JsonParser() {
this.candies = new Hashtable<>();
}
void parse() throws JsonParseException {
// implementation
}
void setParentAndPoints() {
// implementation
}
}
- **单元格类**:该类代表游戏矩阵中的一个单元格。每个单元格包含一个可以粉碎的糖果。它还包含有关如何粉碎、如何重新配置矩阵以及如何获得积分的信息。
class Cell {
Candy candy;
int positionX;
int positionY;
Cell() {
// implementation
}
Cell(Candy candy, int positionX, int positionY) {
// implementation
}
void crush(CellPool pool, Cell[][] cellMatrix) {
// implementation
}
// other methods...
}
- **CandyGame 类**:该类包含游戏继续的规则。
class CandyGame {
Cell[][] cells;
CellPool pool;
int totalPoints;
CandyGame(int num, CellPool pool) {
// implementation
}
boolean continueRound() {
// implementation
}
// other methods...
}
- **CellPool 类**:该类是一个池,它重复使用已粉碎的糖果单元格,而不是重复创建新的单元格。
class CellPool {
int pointer;
List<Cell> pool;
Candy[] randomCode;
CellPool(int num) {
// implementation
}
void addNewCell(Cell c) {
// implementation
}
Candy[] assignRandomCandytypes() {
// implementation
}
Cell getNewCell() {
// implementation
}
}
- **App 类**:该类包含启动游戏的
main
方法。
@Slf4j
public class App {
public static void main(String[] args) {
var givenTime = 50; //50ms
var toWin = 500; //points
var pointsWon = 0;
var numOfRows = 3;
var start = System.currentTimeMillis();
var end = System.currentTimeMillis();
var round = 0;
while (pointsWon < toWin && end - start < givenTime) {
round++;
var pool = new CellPool(numOfRows * numOfRows + 5);
var cg = new CandyGame(numOfRows, pool);
if (round > 1) {
LOGGER.info("Refreshing..");
} else {
LOGGER.info("Starting game..");
}
cg.printGameStatus();
end = System.currentTimeMillis();
cg.round((int) (end - start), givenTime);
pointsWon += cg.totalPoints;
end = System.currentTimeMillis();
}
LOGGER.info("Game Over");
if (pointsWon >= toWin) {
LOGGER.info("" + pointsWon);
LOGGER.info("You win!!");
} else {
LOGGER.info("" + pointsWon);
LOGGER.info("Sorry, you lose!");
}
}
}
让我们分解App
类中发生的事情。
main
方法是应用程序的入口点。它首先初始化几个变量
givenTime
设置为 50 毫秒。这是游戏的时限。toWin
设置为 500 分。这是赢得游戏的目标得分。pointsWon
初始化为 0。此变量跟踪到目前为止获得的总积分。numOfRows
设置为 3。这是游戏网格中的行数。start
和end
都设置为以毫秒为单位的当前系统时间。这些变量用于跟踪经过的时间。round
初始化为 0。此变量跟踪当前的回合数。
游戏进入一个循环,该循环一直持续到玩家获得足够的分数(
pointsWon >= toWin
)或时间限制已到(end - start < givenTime
)。在每一轮开始时,都会创建一个新的
CellPool
和CandyGame
。CellPool
用基于游戏网格中单元格数量(numOfRows * numOfRows + 5
)的大小进行初始化。CandyGame
用行数和CellPool
进行初始化。如果不是第一轮,则会记录一条“刷新中...”消息。如果是第一轮,则会记录一条“开始游戏...”消息。
通过调用
cg.printGameStatus()
打印当前的游戏状态。end
时间更新为当前系统时间。通过调用
cg.round((int) (end - start), givenTime)
进行游戏回合。经过的时间和时间限制作为参数传递。回合中获得的积分将添加到总积分中。
end
时间再次更新为当前系统时间。循环结束后,会记录一条“游戏结束”消息。
如果获得的总积分大于或等于目标得分,则会记录一条获胜消息。否则,会记录一条失败消息。
这是一个类似于糖果粉碎游戏的简化版本,玩家在给定的时间限制内尝试获得尽可能多的积分。游戏以回合进行,玩家的分数和经过的时间在整个游戏中进行跟踪。
控制台输出
14:36:14.453 [main] INFO com.iluwatar.typeobject.App -- Starting game..
14:36:14.455 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- cherry |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- mango |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- orange gum |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- orange gum |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- purple popsicle |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- purple popsicle |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame -- mango |
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.458 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.459 [main] INFO com.iluwatar.typeobject.CandyGame -- +20 points!
...
...
...
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- cherry |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- mango |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- orange gum |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- purple popsicle |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- orange gum |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- +20 points!
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- orange gum |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- cherry |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- purple popsicle |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- green jellybean |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- mango |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- orange gum |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- purple popsicle |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame -- orange gum |
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.CandyGame --
14:36:14.465 [main] INFO com.iluwatar.typeobject.App -- Game Over
14:36:14.465 [main] INFO com.iluwatar.typeobject.App -- 660
14:36:14.465 [main] INFO com.iluwatar.typeobject.App -- You win!!
在此实现中,类型对象模式允许灵活创建糖果
对象。每个糖果的类型在运行时通过解析 JSON 文件确定,这使得在无需重新编译代码的情况下轻松添加、修改或删除糖果类型成为可能。
何时在 Java 中使用类型对象模式
这种模式可以用在以下情况下
- 当需要创建可扩展的相关类集合而不修改现有代码时使用。
- 非常适合在需要在运行时或以灵活方式定义类型及其行为的场景中使用。
- 适用于类型数量很多且可能会随着时间推移而变化的情况。
- 不同“类型”的对象之间的差异在于数据,而不是行为。
类型对象模式 Java 教程
Java 中类型对象模式的现实世界应用
- Java 集合框架:利用各种集合类型,例如 List、Set 和 Map。
- 图形库:使用特定属性和行为定义不同的形状。
- 游戏开发:创建具有独特属性和行为的不同类型的角色或物品。
类型对象模式的优缺点
优点
- 提高代码的灵活性和可扩展性。
- 简化了添加新类型而不修改现有代码的过程。
- 通过组织相关行为和属性,增强了代码的可读性。
缺点
- 如果管理不当,可能会增加复杂性。
- 由于动态类型检查和处理,可能会导致性能开销。