Java游戏可收集物品系统避免违反开闭原则的设计模式咨询
核心问题分析
当前实现的核心矛盾是收集逻辑和具体职业类型硬绑定,强制类型转换导致新增兼容职业时必须修改Spell类代码,违反开闭原则。
可选解决方案
方案1:能力标记接口(轻量改造成本,适合大多数场景)
通过接口标记拥有特殊收集能力的职业,完全避免和具体职业类耦合,新增兼容职业无需修改现有物品代码:
- 定义施法者能力接口
public interface SpellCaster { Set<Collectable> getSpellBook(); }
- 所有可收集法术的职业实现该接口
public class Wizard extends Character implements SpellCaster { private Set<Collectable> spellBook = new HashSet<>(); @Override public Set<Collectable> getSpellBook() { return spellBook; } } // 后续新增Warlock职业直接实现接口即可,无需修改Spell代码 public class Warlock extends Character implements SpellCaster { private Set<Collectable> spellBook = new HashSet<>(); @Override public Set<Collectable> getSpellBook() { return spellBook; } }
- 修改Spell类的收集逻辑,仅校验接口实现,无需硬转具体职业类
public class Spell extends Collectable { @Override public void collect(Character character) { // Java 16+支持模式匹配写法,低版本可手动强转SpellCaster if (character instanceof SpellCaster caster) { caster.getSpellBook().add(this); return; } // 自定义不兼容逻辑:比如提示无法收集、自动丢弃等 throw new IllegalArgumentException(character.getClass().getSimpleName() + " 无法收集法术类道具"); } }
方案2:访问者模式(适合收集逻辑复杂的重交互场景)
如果后续大量物品都有职业专属收集/使用逻辑,可通过访问者模式彻底消除类型转换,实现双向解耦:
- 定义访问者接口
public interface CollectableVisitor { void visit(Wizard wizard); void visit(Warrior warrior); // 新增职业时仅需在这里添加对应visit方法 }
- 修改Character基类,增加访问入口
public abstract class Character { public Set<Collectable> inventory = new HashSet<>(); public abstract void accept(CollectableVisitor visitor); public void collect(Collectable collectable) { collectable.collect(this); } }
- 各个职业实现访问入口
public class Wizard extends Character { public Set<Collectable> spellBook = new HashSet<>(); @Override public void accept(CollectableVisitor visitor) { visitor.visit(this); } } public class Warrior extends Character { @Override public void accept(CollectableVisitor visitor) { visitor.visit(this); } }
- Collectable基类实现访问者接口
public abstract class Collectable implements CollectableVisitor { public abstract void collect(Character character); }
- Spell类实现无类型转换的收集逻辑
public class Spell extends Collectable { @Override public void collect(Character character) { character.accept(this); } @Override public void visit(Wizard wizard) { wizard.spellBook.add(this); } @Override public void visit(Warrior warrior) { throw new IllegalArgumentException("战士无法收集法术类道具"); } }
选型建议
- 仅少数道具存在职业收集限制时选方案1,代码量小、维护成本低
- 大量道具存在职业专属交互逻辑时选方案2,可扩展性更强
内容的提问来源于stack exchange,提问作者Mangos
相关产品推荐
相关产品推荐

