You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决两个插件间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机制实现自动发现:

  1. 在Manhunt插件的resources/META-INF/services目录下创建一个文件,文件名是公共接口的全类名:com.yourpackage.minigame.api.MinigameFactory
  2. 文件内写入ManhuntFactory的全类名:com.yourpackage.manhunt.ManhuntFactory

这样MinigameManager启动时调用loadFactoriesAutomatically()就能自动加载所有子游戏的工厂,完全无需修改主插件代码。

为什么这样能解决循环依赖?

  • 主插件(MinigameManager)只依赖公共API的抽象接口,不再直接引用Manhunt类
  • 子游戏插件(Manhunt)只依赖公共API的抽象接口,不再直接引用MinigameManager类
  • 两者通过抽象层交互,完全解耦,新增子游戏时只需编写实现类和工厂,打包成Jar放入插件目录即可,无需修改主插件代码

内容的提问来源于stack exchange,提问作者Frank W

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:29:44