news 2026/10/22 7:24:58

设计模式组合实战:一个方法干5件事的订单代码,今天4个模式同台拆线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式组合实战:一个方法干5件事的订单代码,今天4个模式同台拆线

作者:鱼宵 | 「Java 后端实战精通营」设计模式系列・第 9 篇

一、真实场景:第 1 篇那份烂代码,今天该动手术了

第 1 篇开篇,我贴过一段外卖订单代码:一个processOrder方法把校验、算优惠、分类型、发通知、记审计5 件事全塞在里头,当时一口气数出 6 处毛病——魔法数字、魔法字符串、一个方法干 5 件事、加需求就改老代码、直接 new、字段 public 裸奔加静默失败。

那篇结尾我放了三句狠话,今天回头看一句都没收:

“第 9 课把 5 件事拆成责任链。”
“一个方法干 5 件事 → 第 9 课责任链组合实战。”
“你现在看到的这份外卖烂代码,就是后面 9 篇的手术对象。”

前 8 课我们一篇磨一把手术刀:第 3 课工厂收拾魔法数字、第 5 课策略干掉优惠 if-else、第 6 课观察者拆掉通知 if 连锁、第 7 课责任链拆校验、第 8 课代理包横切逻辑。每把刀都单独练过,却从没在同一个病人身上一起用过。

今天第 9 课,该把那份烂代码推上手术台——不是再来一个新模式,而是把前面练过的 4 个模式拼成一条能跑的订单全生命周期链路:谁管顺序、谁管环节、谁管算法、谁管广播,一刀一刀切给你看。

代码全在design-patterns仓库的lesson-09里,clone 跑一遍,那 5 件事怎么拆开又怎么拼回去,控制台当场演给你看。

二、核心原理:4 个模式在一条链上,各管哪一段

2.1 为什么不能"一个模式打天下"

真实业务里,一个订单从进来到成交,要校验、算钱、扣库存、发通知、记审计——这一串活,你想用单个模式全包圆?没戏。因为这几件事变化的方式根本不一样:

  • 校验、算优惠、扣库存这几步,顺序固定但环节可能增删→ 责任链最合适;
  • "怎么算优惠"有满减、折扣、不打折好几种,要能互换 → 策略最合适;
  • "先校验、再算钱、再扣库存、最后通知"这个大顺序不能乱→ 模板方法把骨架焊死;
  • 通知有短信、邮件、归档,加渠道不该动主流程→ 观察者广播出去。

类比一条食品加工流水线。模板方法是整体布局(先清洗→切割→包装→质检,顺序焊死);责任链是每个工位可插拔(加个杀菌工位就多一段);策略是切割工位内部怎么切,切块还是切丝随便换;观察者是包装完广播一声"货好了",仓库物流客服各自去忙。四个模式各管一段,谁也不抢谁的活。

面试一句话先背下来:模板方法管"先后",责任链管"环节",策略管"算法",观察者管"广播"。

2.2 分工表(先背这张,再看代码)

模式在本课链路里管什么它应对的"变化"对应类
模板方法钉死订单处理的宏观时序骨架流程顺序不变,细节子类填AbstractOrderProcessTemplate
责任链校验/算优惠/扣库存/审计串成可插拔的链环节能增删、能调序、能中途叫停OrderHandler+ 4 个环节类
策略算优惠的算法族(满减/折扣/无)优惠算法能互换DiscountStrategy+ 3 个实现
观察者下单成功后广播给短信/邮件/归档通知渠道能加减,主流程无感OrderEventBus+ 3 个 Listener

2.3 模板方法和责任链,会不会重复?(最容易绕晕的点)

有人会嘀咕:模板方法process()排好序了,责任链里不又排一遍?不重复——模板方法是宏观编排,一个订单走哪 4 步、什么顺序,final process()焊死子类不许改;责任链是第 2 步"处理环节"的可插拔实现,链里几个环节、谁先谁后链自己说了算。说白了模板方法持有一条责任链,两层各管各的。

2.4 两个"审计"为什么是两个类?

本课还有两个带"审计"的类,千万别因为名字像就合并:ProcessingAuditHandler在责任链里同步记"每步走了啥",是流程日志,排障用;ArchiveAuditListener在订单成交后写对账风控账本,是事后归档。触发时机和用途都不同,所以拆开——这就是按变化理由拆分。

三、关键代码:从导演到观众,四个模式各上各的岗

本课工程在lesson-09/src/main/java/com/dp/lesson09/,一共 23 个类但每个都很短。我按"链路从外到内"贴核心骨架,其余类 clone 下来一眼就能看完。

先看责任链的抽象节点——next指针和"怎么往下传"全钉在这:

packagecom.dp.lesson09;/** * 责任链的抽象节点。每个订单处理环节都是它的一个子类。 */publicabstractclassOrderHandler{/** 链上下一个环节,null 表示链尾 */protectedOrderHandlernext;/** 把下一个环节接在自己后面,并返回它,方便 .setNext().setNext() 一路点下去 */publicOrderHandlersetNext(OrderHandlernext){this.next=next;returnnext;}/** 链入口:final 钉死"怎么传",子类别想改 */publicfinalvoidhandle(OrderContextctx){if(ctx.isStopped()){return;// 已被叫停:本环节直接放行,啥也不干}doHandle(ctx);// 本环节自己干活if(next!=null&&!ctx.isStopped()){next.handle(ctx);// 没被叫停才传给下一个}}/** 子类实现:本环节具体干啥 */protectedabstractvoiddoHandle(OrderContextctx);}

这段讲的是"接力棒":ctx是订单随身带的办事单,每个环节干完要么传给next,要么ctx.stop(原因)叫停整条链。叫停后后面每个节点进来先看到stopped=true,直接走人——不用抛异常,也不用写"后面还有几个节点"。

再看模板方法——整条链的"导演",final process()把 4 步焊死:

packagecom.dp.lesson09;/** * 订单处理模板(模板方法模式核心):不管哪种订单,处理顺序固定为 * 前置检查 → 跑责任链 → 发布成功事件 → 收尾。顺序钉死在 final 方法里。 */publicabstractclassAbstractOrderProcessTemplate{protectedfinalOrderEventBuseventBus;// 事件总线,构造时注入protectedAbstractOrderProcessTemplate(OrderEventBuseventBus){this.eventBus=eventBus;}/** 模板方法:final 钉死,子类不许改流程顺序 */publicfinalvoidprocess(Orderorder){OrderContextctx=newOrderContext(order);beforeProcess(order,ctx);// 第1步:子类钩子(预售校数量/团购校人数)if(ctx.isStopped()){// 前置就被拦:不跑后面System.out.println("[中止] "+order.getOrderId()+" 被前置检查拦下:"+ctx.getStopReason());return;}OrderHandlerchain=buildChain();// 第2步:组装责任链并跑chain.handle(ctx);if(ctx.isStopped()){// 链路里被拦:不发事件、不算成交System.out.println("[中止] "+order.getOrderId()+" 在链路中被拦下:"+ctx.getStopReason());return;}eventBus.publish(newOrderEvent(order.getOrderId(),order.getType(),order.getFinalAmount()));// 第3步:观察者广播System.out.println("[完成] 订单 "+order.getOrderId()+" 全链路结束,实付 "+String.format("%.2f",order.getFinalAmount())+" 元");}/** 组装责任链:校验 → 算优惠 → 扣库存 → 链路审计。final 钉死顺序 */protectedfinalOrderHandlerbuildChain(){OrderHandlerhead=newValidationHandler();// 先用变量接住链头!head.setNext(newDiscountHandler(discountStrategy())).setNext(newStockHandler(stockMode())).setNext(newProcessingAuditHandler());returnhead;}protectedabstractDiscountStrategydiscountStrategy();// 抽象方法:本类订单用哪个优惠策略protectedabstractStringstockMode();// 抽象方法:什么扣库存模式protectedvoidbeforeProcess(Orderorder,OrderContextctx){}// 钩子:默认空,需要才重写}

这里有个本课真踩过的坑:setNext()返回的是"下一个节点",new A().setNext(B).setNext(C)整条表达式最后求值是 C(链尾)。你图省事直接return new A().setNext(B)...,拿到的 chain 直接指向链尾,前面校验、算优惠、库存全部静默不执行,还不报错——第一版我就这么翻过车,负数金额订单都"成交"了。正确姿势:先用变量head接住链头,一路接完再return head。

责任链的"算优惠"环节自己不懂怎么算,它持一个策略——这就是策略嵌进链里的接缝:

packagecom.dp.lesson09;/** 折扣策略接口:每一种优惠算法就是一个实现类,想换算法就换类 */publicinterfaceDiscountStrategy{doublediscount(Orderorder);// 算优惠后实付Stringname();// 中文名,打印日志用}
packagecom.dp.lesson09;/** 满减策略:满 100 减 20。规则要改(比如满 200 减 50)只动这一个文件 */publicclassFullReductionStrategyimplementsDiscountStrategy{privatestaticfinaldoubleTHRESHOLD=100.0;// 满减门槛privatestaticfinaldoubleREDUCTION=20.0;// 减多少@Overridepublicdoublediscount(Orderorder){doubleoriginal=order.getOriginalAmount();// 原价 = 单价 × 数量if(original<THRESHOLD){returnoriginal;// 没到门槛不减,绝不减成负数}returnoriginal-REDUCTION;}@OverridepublicStringname(){return"满100减20";}}

另外两个实现PercentDiscountStrategy(0.8)(八折)和NoDiscountStrategy(原价返回,空对象)不到 40 行。加新券 = 新增一个实现类,主流程一行不改——第 1 篇那个"满减".equals(couponType)的 if-else 连锁,到这彻底拆掉。

观察者那侧,事件总线的心脏就两行:

packagecom.dp.lesson09;importjava.util.ArrayList;importjava.util.List;/** 手写事件总线:一个 List 存订阅者,publish 时 for 循环挨个叫 */publicclassOrderEventBus{privatefinalList<OrderEventListener>listeners=newArrayList<>();/** 订阅:把监听器登记进列表 */publicvoidsubscribe(OrderEventListenerlistener){listeners.add(listener);System.out.println("[EventBus] 监听器已注册:"+listener.listenerName());}/** 发布事件:挨个通知所有订阅者,主流程发完就不管了 */publicvoidpublish(OrderEventevent){System.out.println("[EventBus] 发布下单成功事件:订单 "+event.getOrderId());// 复制一份再遍历,防止某个监听器回调时又去改原列表(并发修改异常)for(OrderEventListenerlistener:newArrayList<>(listeners)){listener.onOrderSuccess(event);}}}

所谓"事件总线",拆开就是个订阅者列表 + for 循环。短信、邮件、归档三个监听器各自实现onOrderSuccess,主流程publish完根本不关心挂了几个——第 1 篇那三个if (needSms)、if (needEmail)、if (needAppPush),到这全没了。

最后是入口Main,看四个模式怎么被拼起来:

packagecom.dp.lesson09;publicclassMain{publicstaticvoidmain(String[]args){System.out.println("========== 第 9 课:模式组合实战 —— 订单全生命周期链路 ==========");// 先造一个事件总线,把三个监听器注册上去。主流程后面只管 publishOrderEventBusbus=newOrderEventBus();bus.subscribe(newSmsOrderListener());// 短信bus.subscribe(newEmailOrderListener());// 邮件bus.subscribe(newArchiveAuditListener());// 成交归档// 场景一:三种订单,各用各的模板子类,跑完整链路AbstractOrderProcessTemplatenormal=newNormalOrderProcessTemplate(bus);normal.process(newOrder("A2001",OrderType.NORMAL,50.0,3,"满减"));// 50×3=150,满减20AbstractOrderProcessTemplatepresale=newPresaleOrderProcessTemplate(bus);presale.process(newOrder("A2002",OrderType.PRESALE,88.0,1,"折扣"));// 88×1,八折AbstractOrderProcessTemplategroup=newGroupBuyOrderProcessTemplate(bus);group.process(newOrder("A2003",OrderType.GROUP,120.0,5,"无"));// 120×5=600,不叠券// 场景二:两种被拦下的异常单,看钩子和责任链怎么早停newPresaleOrderProcessTemplate(bus).process(newOrder("A2004",OrderType.PRESALE,88.0,0,"折扣"));// 预售数量0newNormalOrderProcessTemplate(bus).process(newOrder("A2005",OrderType.NORMAL,-10.0,1,"满减"));// 金额-10// 场景三:新总线只注册短信,主流程一行不改,通知渠道自动变少OrderEventBusleanBus=newOrderEventBus();leanBus.subscribe(newSmsOrderListener());newNormalOrderProcessTemplate(leanBus).process(newOrder("A2006",OrderType.NORMAL,50.0,2,"满减"));}}

调用方全程只跟"抽象模板"打交道,三种订单各自决定策略和库存模式,共用一个总线。换优惠算法、加减通知渠道、插一个环节,分别只改一处——这就是"组合而不是堆砌"。

四、完整演示 + 实测输出

clone 下来后,在lesson-09目录执行(Windows + PowerShell):

cd C:\Users\haoji\Desktop\课程\design-patterns-journey\lesson-09$env:JAVA_HOME="C:\jdk\jdk-17.0.12"mvn clean install-DskipTests

实测输出(编译段):

[INFO] BUILD SUCCESS [INFO] Total time: 1.729 s

编译通过后运行(PATH 上的 java 可能是 JDK8,报 UnsupportedClassVersionError 就用全路径):

[Console]::OutputEncoding=[System.Text.Encoding]::GetEncoding(936)&"C:\jdk\jdk-17.0.12\bin\java.exe"-cp".\target\classes"com.dp.lesson09.Main

实测输出(本机真实跑出来的):

========== 第 9 课:模式组合实战 —— 订单全生命周期链路 ========== [EventBus] 监听器已注册:短信通知 [EventBus] 监听器已注册:邮件通知 [EventBus] 监听器已注册:成交归档审计 ========== 场景一:三种正常订单跑完整链路 ========== ---------- 开始处理:普通单 A2001 ---------- [校验] 订单 A2001 通过校验 [算优惠] 订单 A2001 用【满100减20】:原价 150.00 -> 实付 130.00 [扣库存] 订单 A2001(普通单):立即扣减库存,数量 3 [链路审计] 订单 A2001 处理流水: 步骤1:校验通过:订单号=A2001,原价=150.00 步骤2:算优惠:策略=满100减20,原价=150.00,实付=130.00 步骤3:扣库存:模式=立即扣减库存,数量=3 [EventBus] 发布下单成功事件:订单 A2001 [短信] 您的订单 A2001 已受理,实付 130.00 元 [邮件] 已向用户邮箱发送订单详情:订单 A2001,金额 130.00 元 [成交归档] 已写入对账/风控库:订单 A2001(普通单)成交,金额 130.00 元 [完成] 订单 A2001 全链路结束,实付 130.00 元 ---------- 开始处理:预售单 A2002 ---------- [校验] 订单 A2002 通过校验 [算优惠] 订单 A2002 用【折扣8.0折】:原价 88.00 -> 实付 70.40 [扣库存] 订单 A2002(预售单):只锁库存不扣减(到货后补尾款再扣),数量 1 [链路审计] 订单 A2002 处理流水: 步骤1:校验通过:订单号=A2002,原价=88.00 步骤2:算优惠:策略=折扣8.0折,原价=88.00,实付=70.40 步骤3:扣库存:模式=只锁库存不扣减(到货后补尾款再扣),数量=1 [EventBus] 发布下单成功事件:订单 A2002 [短信] 尊敬的用户,您的预售订单 A2002 已锁库存,到货后将通知您付尾款 [邮件] 已向用户邮箱发送订单详情:订单 A2002,金额 70.40 元 [成交归档] 已写入对账/风控库:订单 A2002(预售单)成交,金额 70.40 元 [完成] 订单 A2002 全链路结束,实付 70.40 元 ---------- 开始处理:团购单 A2003 ---------- [校验] 订单 A2003 通过校验 [算优惠] 订单 A2003 用【无优惠】:原价 600.00 -> 实付 600.00 [扣库存] 订单 A2003(团购单):按团购人数批量扣减库存,数量 5 [链路审计] 订单 A2003 处理流水: 步骤1:校验通过:订单号=A2003,原价=600.00 步骤2:算优惠:策略=无优惠,原价=600.00,实付=600.00 步骤3:扣库存:模式=按团购人数批量扣减库存,数量=5 [EventBus] 发布下单成功事件:订单 A2003 [短信] 您的订单 A2003 已受理,实付 600.00 元 [邮件] 已向用户邮箱发送订单详情:订单 A2003,金额 600.00 元 [成交归档] 已写入对账/风控库:订单 A2003(团购单)成交,金额 600.00 元 [完成] 订单 A2003 全链路结束,实付 600.00 元 ========== 场景二:两种被链路拦下的异常订单 ========== ---------- 开始处理:预售单 A2004 ---------- [中止] A2004 被前置检查拦下:预售订单 A2004 数量非法,前置检查不通过 ---------- 开始处理:普通单 A2005 ---------- [中止] A2005 在链路中被拦下:订单 A2005 原价为 -10.0,非法金额 ========== 场景三:组合而不是堆砌 —— 通知渠道只动注册处 ========== [EventBus] 监听器已注册:短信通知 (新总线只注册了短信,没注册邮件和归档) ---------- 开始处理:普通单 A2006 ---------- [校验] 订单 A2006 通过校验 [算优惠] 订单 A2006 用【满100减20】:原价 100.00 -> 实付 80.00 [扣库存] 订单 A2006(普通单):立即扣减库存,数量 2 [链路审计] 订单 A2006 处理流水: 步骤1:校验通过:订单号=A2006,原价=100.00 步骤2:算优惠:策略=满100减20,原价=100.00,实付=80.00 步骤3:扣库存:模式=立即扣减库存,数量=2 [EventBus] 发布下单成功事件:订单 A2006 [短信] 您的订单 A2006 已受理,实付 80.00 元 [完成] 订单 A2006 全链路结束,实付 80.00 元 ========== 四个模式在链路里的分工 ========== 模板方法 AbstractOrderProcessTemplate.process() —— 钉死宏观时序 | |-- 1. beforeProcess() 钩子(子类:预售校时间/团购校人数) |-- 2. 责任链 chain.handle(ctx) —— 校验->算优惠->扣库存->链路审计 | | ValidationHandler (校验) | | DiscountHandler (内部委托 DiscountStrategy 策略) | | StockHandler (普通/预售/团购 三种扣法) | | ProcessingAuditHandler(流程日志) |-- 3. eventBus.publish(下单成功事件) —— 观察者广播 | | SmsListener 短信 | | EmailListener 邮件 | | ArchiveAuditListener 成交归档 |-- 4. 收尾打印 ========== 演示结束:四个模式各管一段,换某段实现只动一处 ==========

输出解读,重点看两个反差:

  • 两个异常单,拦的位置不一样:A2004(预售数量 0)连责任链都没进,被模板的beforeProcess钩子就拦下;A2005(金额 -10)过了钩子,却在校验环节被责任链stop,后面算优惠、通知、归档全部跳过。第 1 篇那种"订单没处理也没报错"的静默失败,再也不会出现。
  • 场景三最能说明解耦:新总线只注册短信,A2006 就只收到短信;但你往上翻——链路四步一步不少(校验/优惠/库存/审计全打印了),只是事件订阅者少了俩。主流程一行没动,链路和通知彻底分家。

五、挑战题(改参数会怎样?答案在源码里)

  1. ⭐ 把普通单 A2001 的数量从3改成0(别的不动),再跑一遍——它会像 A2004 那样被"前置检查"拦下吗?仔细看NormalOrderProcessTemplate的beforeProcess()到底有没有重写。

  2. ⭐⭐ 把团购单 A2003 的数量从5改成2,再跑一遍——这条订单会走完链路、收到短信和归档吗?去GroupBuyOrderProcessTemplate里找那个成团人数常量,答案就在那一行。

  3. ⭐⭐ 场景三里 leanBus 只注册了短信。如果你把new SmsOrderListener()换成new EmailOrderListener()(只注册邮件),再跑 A2006——它的实付金额还是不是 80.00?为什么"通知变了"但"算出来的钱"一点没变?

三题答案都藏在lesson-09的源码里,clone 下来改一行参数就能验证。跑出来和你想的不一样?评论区贴出来一起查。

六、生产环境进阶:手写是为了看穿原理,生产直接用现成的

真实项目里,这套组合你早就天天在用——只是没从这个视角看:

  • SpringMVC 的DispatcherServlet就是"模板方法 + 责任链 + 策略"链路:doDispatch()顺序焊死(模板方法),HandlerInterceptor的preHandle/postHandle沿链传递(责任链),HandlerAdapter按类型挑实现(策略)。本课手写的四个角色,Spring 一个DispatcherServlet全用上了。
  • Spring 事务 + 事件:@Transactional是个"事务模板";事务方法里 publish 事件、监听器去查这条订单会查不到(事务还没提交),Spring 用@TransactionalEventListener(AFTER_COMMIT)让事件等提交后再触发,本课手写的 EventBus 只是"发布即同步通知"的最简版。

一句忠告:别在生产里从零手写事件总线或拦截器框架。本课手写 30 行是让你看穿"next 指针和 stop 怎么转",上线照 Spring 现成方案来。

七、面试回答模板

Q1:多个设计模式怎么在一个真实业务里分工不打架?

一句话结论:按"哪段代码会怎么变"来划——流程顺序不变用模板方法、环节要增删用责任链、算法要互换用策略、副作用要广播用观察者。展开:模板方法钉骨架、责任链管可插拔环节、策略管优惠算法、观察者管下单后广播,本文 2.2 的分工表直接背。

Q2:模板方法和责任链都排了一遍顺序,重复吗?

一句话结论:不重复——模板方法是宏观流程图(顺序焊死在 final 方法里),责任链是图里"处理环节"那一步的可插拔实现,模板方法持有它。展开:模板方法不关心链里挂几个 Handler,责任链不关心跑完要不要发事件。本文 2.3 讲过。

Q3:什么时候该上模式,什么时候老老实实写 if-else?

一句话结论:先写 if-else 能跑,等出现"第二种实现 / 第三个分支 / 第二个通知渠道"时再抽象,别为"未来可能"提前上模式。展开:一个需求只出现一次就拆策略,是过度设计;等 if-else 真长得别扭了再抽不迟。

八、总结表:第 1 篇的 6 处坏味道,拆线清单

把第 1 篇列的那 6 处坏味道,一条一条对成本课的落点:

第 1 篇的坏味道当时的病根本课用什么拆的
魔法数字type=1/2/3值写死,含义靠猜OrderType枚举(NORMAL/PRESALE/GROUP),漏分支编译器提醒
魔法字符串"满减"/"折扣"的 if-else加券就要改老方法DiscountStrategy策略族,加券 = 加一个类
一个方法干 5 件事5 个改变理由责任链 4 环节 + 模板方法 4 步,各管一段
加需求就动老代码违反开闭原则加通知渠道 = 加 Listener 注册一行,主流程无感
调用方直接new写死面向实现编程面向抽象AbstractOrderProcessTemplate编程,子类可换
字段 public 裸奔 + 静默失败谁都能改、出错不吭声Order全 private + getter;ctx.stop()显式叫停,再也不静默

九、关于这个系列

本文是「Java 后端实战精通营」设计模式系列第 9 篇。第 1 篇那份外卖烂代码,到这一篇算是做完了整台手术——前面每课磨一把刀,今天四把刀在同一个病人身上配合着用,你终于看清"模式不是背 23 个名字,而是这段代码该用哪张处方"。

  • 完整可运行工程:https://gitee.com/j67mk2/design-patterns(本课源码在lesson-09/,主类com.dp.lesson09.Main,23 个类全在里面)
  • 下一篇预告(系列完结篇):《支付网关收单平台:把 9 课的模式,落成一个能拿去面试讲的完整系统》。第 10 课把它升级成真实可跑的支付网关——多渠道路由用策略、渠道回调用模板方法、对账通知用观察者,再加签名验签、幂等防重、异常兜底这些企业级要求,做完就能直接写进简历。

互动提问:如果现在让你把自己项目里一个"干了 3 件事以上"的方法拆开,你最先想用哪个模式?为什么?评论区聊聊,我帮你看看该挂在链路哪一环。

本文部分内容由 AI 辅助生成

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

渲染流水线

8.3 渲染流水线渲染流水线是浏览器将 DOM、CSSOM 转化为屏幕可视化像素的完整执行流程&#xff0c;与事件循环深度协同&#xff0c;按固定帧率周期性执行。完整的渲染流水线分为样式计算、布局、绘制、合成四个核心阶段&#xff0c;不同阶段对应不同的性能开销。配合 requestAn…

作者头像 李华
网站建设 2026/10/22 7:23:56

SUAPP AI 云端建模成果:导出 skp/glb 之后要检查的三件事

下载之后&#xff0c;建议先检查结构能否按需修改、几何是否符合设计输入、材质显示是否适合交付用途&#xff0c;再决定模型进入哪一步。本文围绕模型导出、可编辑模型与成果校核展开&#xff0c;供使用AI建模软件的设计人员安排人工检查&#xff1b;三件事属于项目方法建议&a…

作者头像 李华
网站建设 2026/10/22 7:23:25

为什么 LLM 会产生幻觉?——token 级别的解释

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

作者头像 李华
网站建设 2026/10/22 7:20:01

遇到上头的买家怎么接?情绪降温三步与补偿的边界

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

作者头像 李华
网站建设 2026/10/22 7:18:09

On Explaining (Large) Language Models For Code Using Global Code-Based Explanations

文章主要内容总结 本文提出了一种名为CodeQ的可解释性方法,旨在通过全局代码解释来分析大型语言模型(LM4Code)的代码生成决策过程。CodeQ通过三个核心步骤实现: Rationalization:提取影响代码预测的关键输入令牌子集(rationale),通过贪心算法逐步选择对目标令牌概率贡…

作者头像 李华
网站建设 2026/10/22 7:17:09

2026免费低代码平台TOP5,中小企业数字化工具盘点

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

作者头像 李华