Java如何优雅从HashMap获取指定类型规则对象优化调用写法
问题根因
你当前需要强制类型转换、静态方法耦合的核心原因是Rule接口没有定义统一的实例方法契约,所有规则的执行逻辑都写成了静态方法,接口完全没起到抽象层的作用。
优化方案
1. 重构Rule接口,定义统一行为契约
把所有规则共有的执行方法定义为接口的实例方法签名,删掉所有实现类里resolveRule的static修饰符,让实现类重写接口方法:
public interface Rule { // 通用规则执行方法,入参与你现有逻辑保持一致 void resolveRule(Tile tilePosee, FrameTT frame, Board board); // 针对部分规则需要多参数的场景,加默认重载方法,避免强转 default void resolveRule(Tile self, Tile target, Direction dir, FrameTT frame, Board board) { throw new UnsupportedOperationException("当前规则不支持邻接格子结算逻辑"); } // 注入规则容器的默认方法,不需要依赖其他规则的实现类不用重写 default void setRuleRegistry(Map<RuleType, Rule> registry) {} }
改完之后从Map里取出的Rule实例可以直接调用resolveRule,完全不需要强制类型转换,你之前的强转代码可以直接删掉:
// 废弃原有强转写法 callRule(ruleList, COMBO).resolveRule(tileDirection, frame, board);
2. 解决规则间互相调用的耦合问题
你之前用静态方法主要是为了跨规则调用,这里不要写死类名做静态调用,两种低改动的实现方式可选:
方式A:注入规则注册表(耦合度最低)
初始化规则Map时,把容器引用注入到每个规则实例中,规则内部直接通过容器拿依赖的其他规则即可,不需要静态耦合:
// 初始化规则Map时建议用EnumMap,枚举作为键时性能比HashMap更好 private Map<RuleType, Rule> ruleList = new EnumMap<>(RuleType.class); private void createRuleMap() { // 先注册所有规则实例,规则本身无状态,全局初始化一次即可 ruleList.put(COMBO, new RuleCombo()); ruleList.put(ELEMENTALE, new RuleElementales()); ruleList.put(IDENTIQUE, new RuleIdentique()); ruleList.put(MEMEMUR, new RuleMemeMur()); ruleList.put(PLUS, new RulePlus()); // 统一注入规则容器 ruleList.values().forEach(rule -> rule.setRuleRegistry(ruleList)); }
以RuleCombo为例,改造后的代码如下:
public class RuleCombo implements Rule { private Map<RuleType, Rule> ruleRegistry; @Override public void setRuleRegistry(Map<RuleType, Rule> registry) { this.ruleRegistry = registry; } @Override public void resolveRule(Tile tilePosee, FrameTT frame, Board board) { if (ruleExisting(IDENTIQUE)) { ruleRegistry.get(IDENTIQUE).resolveRule(tilePosee, frame, board); } if (ruleExisting(PLUS)) { ruleRegistry.get(PLUS).resolveRule(tilePosee, frame, board); } for (Direction direction : Direction.values()) { Tile tileDirection = getAdjacentTile(tilePosee, direction, board); if (checkTileExist(tileDirection)) { if ((tileDirection.getPlayer() != tilePosee.getPlayer()) && checkTileIsOccupied(tileDirection)) { if (ruleExisting(ELEMENTALE)) { // 直接调用接口重载的多参数方法,不需要强转 ruleRegistry.get(ELEMENTALE).resolveRule(tilePosee, tileDirection, direction, frame, board); } else { cardPoseeWin(tilePosee, tileDirection, direction, frame, board); } } } } } }
方式B:全局规则上下文(现有代码改动最小)
如果不想给每个规则加注入逻辑,可以把规则容器封装成全局上下文,提供静态访问方法,规则直接从上下文取依赖即可:
public class RuleContext { private static final Map<RuleType, Rule> RULE_MAP = new EnumMap<>(RuleType.class); // 游戏初始化时调用一次 public static void init() { RULE_MAP.put(COMBO, new RuleCombo()); RULE_MAP.put(ELEMENTALE, new RuleElementales()); RULE_MAP.put(IDENTIQUE, new RuleIdentique()); RULE_MAP.put(MEMEMUR, new RuleMemeMur()); RULE_MAP.put(PLUS, new RulePlus()); } public static Rule getRule(RuleType type) { Rule rule = RULE_MAP.get(type); if (rule == null) throw new IllegalArgumentException("未注册的规则类型:" + type); return rule; } public static boolean hasRule(RuleType type) { return RULE_MAP.containsKey(type); } }
这种方式下,你原有代码里的ruleExisting判断直接替换成RuleContext.hasRule(xxx),调用其他规则直接写RuleContext.getRule(xxx).resolveRule(...)即可,改动量极小。
额外优化建议
- 你当前封装的
callRule静态方法没有额外价值,直接从Map/上下文获取规则即可,不需要多包一层 - 把
checkTileExist、getAdjacentTile、cardPoseeWin这类所有规则共用的工具逻辑抽到单独的RuleUtils或BoardUtils类中,不要散落在各个规则类里重复实现 - 所有规则实例都是无状态的,全局只初始化一次即可,不需要每次执行规则时创建新实例
内容的提问来源于stack exchange,提问作者Simon Debelmas
相关产品推荐
相关产品推荐

