news 2026/10/4 1:40:35

Java 设计模式之 Service to Worker:Web 应用中控制流与视图管理的分离实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 设计模式之 Service to Worker:Web 应用中控制流与视图管理的分离实战
  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

Service to Worker(服务到工作者)是一种架构型设计模式,它将 Web 应用中的请求处理、控制流与视图管理分离为职责清晰的组件,本质上是 Dispatcher View 与 Service Locator 思想的结合。本文以 java-design-patterns 仓库中的 service-to-worker 模块为骨架,完整解析该模式在 Java 应用中的落地方式、全部核心类的源码实现、测试验证方法以及适用场景,帮助读者掌握一套可维护、可扩展的 Web 表现层分层方案。

模式意图:处理逻辑与视图的分离

在典型的 Web 应用中,请求往往需要经过参数校验、业务处理、结果组装等多道工序,才能最终渲染到页面。如果这些逻辑全部堆在页面或控制器里,会导致代码耦合、难以测试、多人协作互相踩踏。

Service to Worker 模式的意图正是:将处理(processing)、控制流(control flow)与视图管理(view management)解耦。它通过组合 Dispatcher View 与 Service Locator 两个既有模式,让控制器只负责接收命令、Dispatcher 负责调度动作并选择视图、Worker(Action)负责更新模型、View 负责渲染展示,从而提升代码的可维护性与可扩展性。

用一句话概括:将 Web 应用中的处理逻辑与视图分离,以提升可维护性和可扩展性(该表述亦见于 service-to-worker/README.md 的 plain words 小节)。

现实世界类比:餐厅中央厨房

想象一家拥有中央厨房和多名服务员的连锁餐厅。顾客下单后,服务员(Controller)接过订单,交给厨房(Service);厨房处理订单、烹饪菜肴,再交回给服务员;服务员最终把菜端给顾客(View)。

这个场景精确映射了 Java Web 应用中的 Service to Worker 结构:后端逻辑(厨房)与前端交互(服务员)相互隔离,各自专注本职。模式实现中,GiantController扮演服务员(控制器),Action(Worker)与Dispatcher扮演厨房调度,GiantView扮演端菜上桌的最终展示层。

模式参与者与整体结构

从 类图(PlantUML 源文件见 service-to-worker.urm.puml)可以看出,模块的核心参与者共 7 个类:

参与者角色核心职责
GiantControllerController拦截请求、接收命令、触发视图刷新
DispatcherDispatcher维护 Worker 列表,执行动作并选择视图
ActionWorker处理用户输入,将其转化为对模型的更新
GiantModelModel持有领域数据(health/fatigue/nourishment)
GiantViewView将模型数据渲染为面向用户的形式
Command请求封装以 record 形式封装一次状态变更指令
App入口组装各组件并驱动整个流程

其协作关系(时序图)为:Controller → Dispatcher → Action → GiantModel → GiantView,形成一条单向、清晰的控制链。App类的源码注释也完整描述了这一流程:

前端控制器拦截所有请求并执行公共功能;它把请求信息交给 Dispatcher,Dispatcher 依据请求与内部模型选择并执行合适的 Action;Action 处理用户输入,将其翻译为对模型的适当更新;模型应用业务规则并持久化数据;最后 Dispatcher 根据用户输入与动作结果选择视图,视图把更新后的模型数据转换成用户可读的形式。

详见 App.java。

程序化示例:完整源码逐类解析

1. GiantController:面向请求的入口

GiantController是模式中的控制器,对外只暴露两个能力:setCommand下发命令、updateView刷新视图。它内部持有 Dispatcher,所有请求都转交给 Dispatcher 处理:

public class GiantController { public Dispatcher dispatcher; public GiantController(Dispatcher dispatcher) { this.dispatcher = dispatcher; } public void setCommand(Command s, int index) { dispatcher.performAction(s, index); } public void updateView(GiantModel giantModel) { dispatcher.updateView(giantModel); } }

源码注释(GiantController.java)指出它是一个“拦截所有请求并执行公共功能的单例式对象”。注意updateView是简化实现,注释明确说明真实场景中 View 可以采用更具体的实现(如 JSP/模板渲染)。

2. Command:不可变命令封装

Java 16 的 record 让命令封装变得极其简洁。Command把一次状态变更所需的三个字段打包为不可变值对象:

public record Command(Fatigue fatigue, Health health, Nourishment nourishment) {}

见 Command.java。其中Fatigue、Health、Nourishment三个枚举复用自仓库的 model-view-controller 模块(该依赖声明于 service-to-worker/pom.xml),取值分别为:

  • Fatigue:ALERT(警觉)、SLEEPING(睡眠)、TIRED(疲惫)
  • Health:DEAD(死亡)、HEALTHY(健康)、WOUNDED(受伤)
  • Nourishment:HUNGRY(饥饿)、SATURATED(饱食)、STARVING(挨饿)

3. Action(Worker):把命令翻译为模型更新

Action是模式中的 Worker,负责把用户输入(Command)翻译为对GiantModel的具体更新。每个 Action 绑定一个 GiantModel:

public class Action { public GiantModel giant; public Action(GiantModel giant) { this.giant = giant; } public void updateModel(Command command) { setFatigue(command.fatigue()); setHealth(command.health()); setNourishment(command.nourishment()); } public void setHealth(Health health) { giant.setHealth(health); } public void setFatigue(Fatigue fatigue) { giant.setFatigue(fatigue); } public void setNourishment(Nourishment nourishment) { giant.setNourishment(nourishment); } }

源码注释(Action.java)将其定义为"可以处理用户输入并对模型执行特定更新的 Worker"。

4. Dispatcher:调度中枢

Dispatcher是模式的核心调度器,封装了 Worker 选择与视图选择两大职责:

public class Dispatcher { @Getter private final GiantView giantView; private final List<Action> actions; public Dispatcher(GiantView giantView) { this.giantView = giantView; this.actions = new ArrayList<>(); } void addAction(Action action) { actions.add(action); } public void performAction(Command s, int actionIndex) { actions.get(actionIndex).updateModel(s); } public void updateView(GiantModel giantModel) { giantView.displayGiant(giantModel); } }

关键点(Dispatcher.java):

  • 内部维护List<Action>作为 Worker 注册表,addAction负责注册(包级私有);
  • performAction(Command, index)按下标定位 Worker 并执行命令——这就是"依据请求信息选择 Worker"的简化实现;
  • updateView把模型交给GiantView.displayGiant,即"选择并渲染视图"。

源码注释将 Dispatcher 定义为"基于请求信息和/或内部导航模型,封装 Worker 与视图选择的组件",这正是 Service to Worker 中 Dispatcher View 角色的体现。

5. GiantModel:可复用的领域模型

GiantModel对外包装了来自 model-view-controller 模块的领域模型,添加了name属性,并提供对 health/fatigue/nourishment 三态的读写方法以及格式化输出:

GiantModel(String name, Health health, Fatigue fatigue, Nourishment nourishment) { this.name = name; this.model = new com.iluwatar.model.view.controller.GiantModel(health, fatigue, nourishment); } @Override public String toString() { return String.format( "Giant %s, The giant looks %s, %s and %s.", name, model.getHealth(), model.getFatigue(), model.getNourishment()); }

见 GiantModel.java。

6. GiantView:极简渲染层

GiantView借助 Lombok 的@Slf4j注解生成日志器,把模型渲染为日志输出——在真实 Web 工程中,这一层通常对应 JSP、Thymeleaf 模板等视图技术:

@Slf4j public class GiantView { public void displayGiant(GiantModel giant) { LOGGER.info(giant.toString()); } }

见 GiantView.java。

入口程序与运行输出

App.main完整演示了组件的组装与两轮"显示 → 下发命令 → 再显示"的交互流程:

public class App { public static void main(String[] args) { var giant1 = new GiantModel("giant1", Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); var giant2 = new GiantModel("giant2", Health.DEAD, Fatigue.SLEEPING, Nourishment.STARVING); var action1 = new Action(giant1); var action2 = new Action(giant2); var view = new GiantView(); var dispatcher = new Dispatcher(view); dispatcher.addAction(action1); dispatcher.addAction(action2); var controller = new GiantController(dispatcher); controller.updateView(giant1); controller.updateView(giant2); controller.setCommand(new Command(Fatigue.SLEEPING, Health.HEALTHY, Nourishment.STARVING), 0); controller.setCommand(new Command(Fatigue.ALERT, Health.HEALTHY, Nourishment.HUNGRY), 1); controller.updateView(giant1); controller.updateView(giant2); } }

完整源码见 App.java。运行该程序的对应控制台输出:

12:23:10.895 [main] INFO com.iluwatar.servicetoworker.GiantView -- Giant giant1, The giant looks healthy, alert and saturated. 12:23:10.897 [main] INFO com.iluwatar.servicetoworker.GiantView -- Giant giant1, The giant looks healthy, sleeping and starving. 12:23:10.897 [main] INFO com.iluwatar.servicetoworker.GiantView -- Giant giant2, The giant looks dead, sleeping and starving. 12:23:10.897 [main] INFO com.iluwatar.servicetoworker.GiantView -- Giant giant2, The giant looks healthy, alert and hungry.

可以看到:命令 0 把giant1的疲劳改为SLEEPING、饱食度改为STARVING;命令 1 把giant2的疲劳改为ALERT、饱食度改为HUNGRY——控制器只下发命令,模型状态变更与视图刷新全部由 Dispatcher/Action/View 协作完成。

测试验证:核心行为的自动化保障

模块在 service-to-worker/src/test 下提供了 6 个测试类,覆盖了模式的每条关键链路:

  • DispatcherTest:testPerformAction遍历Nourishment、Fatigue、Health三组枚举的全部 27 种组合构造 Command 并执行,逐一断言模型三态被正确更新(DispatcherTest.java);testUpdateView验证视图刷新不抛异常。
  • GiantControllerTest:验证setCommand确实通过 Dispatcher 将命令传播到模型(GiantControllerTest.java),并验证updateView链路畅通。
  • 另有ActionTest、GiantModelTest、GiantViewTest、AppTest分别覆盖 Worker 更新逻辑、模型读写与格式化、视图日志输出以及入口程序的冒烟执行。

这些测试表明:即便替换 View 实现或新增 Command 类型,只要 Dispatcher 的调度契约不变,控制器层与展示层都可以独立演进。

如何运行与查看

service-to-worker 是 Maven 多模块工程的一个子模块(groupId: com.iluwatar,版本继承自根 pom 的1.26.0-SNAPSHOT),其 pom.xml 通过 maven-assembly-plugin 将com.iluwatar.servicetoworker.App配置为可执行主类。在仓库根目录执行:

./mvnw -pl service-to-worker compile exec:java -Dexec.mainClass=com.iluwatar.servicetoworker.App

即可看到上述控制台输出。也可直接运行根目录的 mvnw 包装脚本进入模块目录执行./mvnw test验证全部测试用例。

何时使用 Service to Worker 模式

根据 service-to-worker/README.md,适合采用该模式的情形包括:

  • 需要将控制器逻辑与视图分离,以提升可维护性,并让团队成员能独立开发应用的不同部分;
  • 适用于采用 MVC 架构的 Java Web 应用;
  • 适用于需要在展示视图之前进行复杂请求处理的场景。

典型误用信号:应用只有简单的"请求 → 页面"直通逻辑,没有复杂的业务处理与多视图选择,此时引入该模式反而徒增分层复杂度。

真实世界应用

该模式在业界有成熟落地:

  • Struts与Spring MVC等基于 Java 的 Web 框架:其 DispatcherServlet / ActionServlet 本质上就是模式中的 Dispatcher,负责把请求路由到对应的 Controller/Handler,再选择视图解析器渲染;
  • 企业级 Web 应用:需要表现层逻辑与业务逻辑清晰隔离的场合。

(以上来自 service-to-worker/README.md 的 Real-World Applications 小节。)

收益与权衡

收益:

  • 通过关注点分离(separation of concerns)提升代码可维护性;
  • 解耦控制器与视图组件,促进团队协作——前后端/表现层与逻辑层可并行开发;
  • 新增视图、修改既有视图更加简单,改动影响面被限制在视图层。

权衡:

  • 增加了应用结构的复杂度——需要理解并维护 Controller、Dispatcher、Action、View 之间的协作关系;
  • 分层架构可能引入额外开销,对于简单应用而言可能过度设计。

与其他模式的关系

  • Model-View-Controller:Service to Worker 是 MVC 的一种特化形态,聚焦于把请求处理与视图管理进一步拆开;本模块的GiantModel、GiantView即直接复用自该模块。
  • Front Controller:常与 Service to Worker 搭配使用,前者负责集中化请求处理与路由(GiantController即扮演了类似的前端控制器角色),后者负责调度 Worker 与选择视图。

小结

Service to Worker 用一个清晰的三段式链路——Controller 收命令、Dispatcher 调度 Worker、View 渲染模型——把 Web 表现层的复杂度拆解为可独立演进、可单独测试的组件。java-design-patterns 仓库的 service-to-worker 模块以不足十个类完整呈现了这一模式,配合 时序图 与 类图 以及覆盖全部链路的单元测试,是一份可直接对照学习、甚至可以扩展到真实 Web 框架实践的最小可运行范本。

  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

相关推荐

上一篇:GalaxyBudsClient脚本系统解析:实验性功能与钩子机制
下一篇:Termwind入门指南:10分钟打造你的第一个PHP命令行美化应用

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 1:39:01

基于MRAM与PIC18F87J50的工业数据记录模块设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:38:43

C#与VisionPro联合开发实战:从集成选型到现场稳定运行排错指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:38:07

Micro-LED光子晶体量产工艺:NIL+ICP+PECVD+PVD四步闭环

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:36:42

线性代数工程化指南:从解方程到SVD的三层实战体系

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:36:28

Unity面试必考设计模式:六大核心模式原理与实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:35:47

MR25H40CDF与PIC18LF46K22的SPI MRAM嵌入式数据存储实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华