基于Hexagonal+Onion架构实现MasterMind游戏的技术疑问
MasterMind 六边形/洋葱架构实现疑问解答
第一个疑问:端口划分与游戏循环的实现
你的端口划分存在方向混淆,核心逻辑的主导权和端口类型对应关系需要调整:
游戏循环的归属:游戏循环必须是应用核心(游戏引擎)的内部逻辑,而非由外部适配器控制转发。核心应独立完成「获取密码→获取猜测→获取评分→判断胜负」的循环直至分出结果,以此保证核心业务逻辑不依赖外部UI或玩家类型。
正确的端口划分:
- Driven/Secondary Ports(核心依赖外部的接口):Coder和Decoder都是核心需要依赖的外部角色,和它们交互的接口均属于此类:
CoderPort:定义核心向Coder获取密码、请求评分的方法(如getSecretCode()、rateGuess(Guess))DecoderPort:定义核心向Decoder获取猜测的方法(如makeGuess())GameFeedbackPort:定义核心向外部输出胜负结果、回合信息的方法(如notifyResult(GameResult)、notifyRoundFeedback(RoundFeedback))
- Driving/Primary Ports(外部驱动核心的入口):仅需一个核心启动入口,比如
GameEnginePort,定义外部触发游戏的方法(如startGame(CoderType, DecoderType))。外部适配器(CLI、Web控制器、后续联网服务)通过调用这个端口启动游戏,核心内部自行运行循环。
- Driven/Secondary Ports(核心依赖外部的接口):Coder和Decoder都是核心需要依赖的外部角色,和它们交互的接口均属于此类:
你之前将「Coder-In/Decoder-In设为Driving Ports」的思路有误——Driving Ports是外部调用核心的入口,而非核心从外部获取数据的接口;核心主动从外部拿数据的行为属于依赖外部,归为Driven Ports范畴。
第二个疑问:Web场景下的Driving Ports必要性
Web场景下仍然需要Driving Ports,原因如下:
Driving Ports的本质是定义「外部如何与核心交互」的契约,和输出方向无关。Web控制器作为Driving Adapter,通过调用核心的Driving Port(如
GameEnginePort.startGame())触发游戏逻辑;当核心需要向用户输出结果时,通过Driven Ports(如GameFeedbackPort)完成,Web适配器实现该端口后,将结果转化为HTTP响应返回给用户。保留Driving Ports能确保核心完全独立于Web技术栈(如HTTP、Spring MVC等),后续若扩展为桌面应用或联网对战,只需新增对应的Driving Adapter即可,核心代码无需修改,符合六边形/洋葱架构的「依赖倒置」原则。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

