- 示例工程
- 教程
【免费下载链接】java-design-patterns
Design patterns implemented in Java
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 个类:
| 参与者 | 角色 | 核心职责 |
|---|---|---|
GiantController | Controller | 拦截请求、接收命令、触发视图刷新 |
Dispatcher | Dispatcher | 维护 Worker 列表,执行动作并选择视图 |
Action | Worker | 处理用户输入,将其转化为对模型的更新 |
GiantModel | Model | 持有领域数据(health/fatigue/nourishment) |
GiantView | View | 将模型数据渲染为面向用户的形式 |
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
相关推荐
终极虚幻引擎存档编辑器:5分钟快速上手uesave-rs完整指南
终极虚幻引擎存档编辑器:5分钟快速上手uesave rs完整指南 你是否曾因游戏存档损坏而痛失数十小时进度?面对虚幻引擎生成的二进制.sav文件,传统文本编辑器
序列化游戏开发开发工具Java 设计模式之 Service Stub(服务桩)模式:以情感分析服务为例实现隔离测试
Java 设计模式之 Service Stub(服务桩)模式:以情感分析服务为例实现隔离测试 Service Stub(服务桩,又称 Service Mock
示例工程教程Java 设计模式实战:Balking 模式在 java-design-patterns 中的实现与并发应用
Java 设计模式实战:Balking 模式在 java design patterns 中的实现与并发应用 Balking(退缩/迟疑)模式是 Java 并发
示例工程教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考