解决两个插件间Manhunt与MinigameManager的循环依赖问题
解决Minigame与Manager的循环依赖,实现模块化插件扩展
你的思路完全正确——**依赖倒置原则(DIP)**就是解决这类问题的核心,我们需要通过抽象层解耦具体实现类之间的直接依赖,让主插件(MinigameManager)和子游戏插件(Manhunt)都依赖抽象,而非彼此的具体类。下面是具体落地步骤:
1. 抽离公共抽象层(API模块)
首先创建一个独立的公共Jar包(比如minigame-api),作为主插件和所有子游戏插件的依赖,里面只放抽象定义,不包含任何具体实现:
// 公共Minigame抽象(可以用接口或抽象类,这里用接口更灵活) public interface Minigame { void startCountdown(); } // 游戏工厂接口:负责创建对应Minigame实例,解耦new操作 public interface MinigameFactory { // 返回该工厂对应的游戏标识(比如"manhunt") String getGameId(); // 根据游戏所需参数创建实例 Minigame create(int gameKey, UUID runner, List<UUID> players); }
这个API模块是所有插件的唯一依赖,主插件和子游戏插件都只依赖它,不再直接依赖彼此。
2. 改造MinigameManager(主插件)
让Manager完全依赖API层的抽象,移除对具体Manhunt类的引用,改为通过工厂来创建游戏实例,同时提供注册/发现工厂的机制:
public class MinigameManager { // 存储游戏标识与对应工厂的映射 private final Map<String, MinigameFactory> gameFactories = new HashMap<>(); private List<UUID> playersQueued; private Random random; private Minigame currentMinigame; // 注册工厂:允许外部插件主动注册自己的游戏工厂 public void registerFactory(MinigameFactory factory) { gameFactories.put(factory.getGameId().toLowerCase(), factory); } // 自动发现工厂(可选,用Java SPI实现无需手动注册) public void loadFactoriesAutomatically() { ServiceLoader<MinigameFactory> loader = ServiceLoader.load(MinigameFactory.class); for (MinigameFactory factory : loader) { gameFactories.put(factory.getGameId().toLowerCase(), factory); } } public void startMinigame(String minigameId, int gameKey) { MinigameFactory factory = gameFactories.get(minigameId.toLowerCase()); if (factory == null) { // 处理未知游戏的逻辑,比如日志提示 return; } // 原来的玩家选择逻辑不变 UUID runner = playersQueued.remove(random.nextInt(playersQueued.size())); // 通过工厂创建游戏实例,而非直接new Manhunt currentMinigame = factory.create(gameKey, runner, new ArrayList<>(playersQueued)); currentMinigame.startCountdown(); playersQueued.clear(); } }
3. 改造Manhunt插件(子游戏插件)
让Manhunt插件依赖公共API层,实现抽象接口,并提供对应的工厂类:
// 实现Minigame接口 public class Manhunt implements Minigame { private int gameKey; private UUID runner; private List<UUID> hunters; // 构造方法保持原有逻辑 public Manhunt(int gameKey, UUID runner, List<UUID> hunters) { this.gameKey = gameKey; this.runner = runner; this.hunters = hunters; } @Override public void startCountdown() { // 你的倒计时逻辑不变 } } // 实现MinigameFactory,负责创建Manhunt实例 public class ManhuntFactory implements MinigameFactory { @Override public String getGameId() { return "manhunt"; } @Override public Minigame create(int gameKey, UUID runner, List<UUID> players) { return new Manhunt(gameKey, runner, players); } }
自动注册工厂(可选,更符合"拖拽新增"需求)
如果不想在插件启动时手动调用registerFactory,可以用Java SPI机制实现自动发现:
- 在Manhunt插件的
resources/META-INF/services目录下创建一个文件,文件名是公共接口的全类名:com.yourpackage.minigame.api.MinigameFactory - 文件内写入ManhuntFactory的全类名:
com.yourpackage.manhunt.ManhuntFactory
这样MinigameManager启动时调用loadFactoriesAutomatically()就能自动加载所有子游戏的工厂,完全无需修改主插件代码。
为什么这样能解决循环依赖?
- 主插件(MinigameManager)只依赖公共API的抽象接口,不再直接引用Manhunt类
- 子游戏插件(Manhunt)只依赖公共API的抽象接口,不再直接引用MinigameManager类
- 两者通过抽象层交互,完全解耦,新增子游戏时只需编写实现类和工厂,打包成Jar放入插件目录即可,无需修改主插件代码
内容的提问来源于stack exchange,提问作者Frank W
相关产品推荐
相关产品推荐

