[{"data":1,"prerenderedAt":1107},["ShallowReactive",2],{"post:\u002Fposts\u002Fswdesign\u002Fsw-design-notes-10":3,"posts:ordered":805},{"id":4,"title":5,"body":6,"category":786,"cover":787,"date":788,"description":789,"extension":790,"featured":791,"meta":792,"navigation":793,"path":794,"seo":795,"series":796,"seriesOrder":797,"stem":798,"tags":799,"__hash__":804},"posts\u002Fposts\u002FswDesign\u002Fsw-design-notes-10.md","软件分析与设计笔记 10：精炼用例模型：include、extend 与泛化",{"type":7,"value":8,"toc":754},"minimark",[9,16,25,30,33,36,58,65,72,76,86,97,102,108,118,122,129,135,142,149,153,159,166,170,180,186,192,198,202,205,211,215,218,224,233,236,247,251,257,260,267,271,369,376,380,386,390,396,399,405,412,416,422,425,436,440,447,452,456,463,467,470,473,492,499,502,506,509,513,519,523,529,533,540,547,550,571,574,626,629],[10,11,12],"blockquote",{},[13,14,15],"p",{},"这是我在学习软件分析与设计时整理的系列笔记。概念部分是我按自己的理解写的，示例与图示都是自己编、自己画的。理解有偏差的地方欢迎指出。",[10,17,18],{},[13,19,20,24],{},[21,22,23],"strong",{},"贯穿案例","：片区家电维修派单系统。08 画了用例图、09 抽了实体，这一篇把 08 那张平铺的图精炼一遍。",[26,27,29],"h2",{"id":28},"_0-用例画完之后重复出现了","0 用例画完之后，重复出现了",[13,31,32],{},"第一版用例图通常是这样的：每个用例各画各的，画完一看，同一件事在好几个用例里各写了一遍。",[13,34,35],{},"贯穿案例里就有三处：",[37,38,39,46,52],"ul",{},[40,41,42,45],"li",{},[21,43,44],{},"「校验工单状态」"," —— 「派单」「申请配件」「回填工单」里各写了一遍；",[40,47,48,51],{},[21,49,50],{},"「撤回申请」"," —— 只在提交后 5 分钟内可以，是「申请配件」的一个分支；",[40,53,54,57],{},[21,55,56],{},"「客服」和「片区经理」"," —— 画了两套用例，但其中一大半是同一个。",[13,59,60,61,64],{},"用例模型的精炼（refining the use case model）要解决的就是这类重复，以及",[21,62,63],{},"把可选流程从主流程里拆出来","。有三种关系可以用。",[13,66,67],{},[68,69],"img",{"alt":70,"src":71},"精炼用例模型的三种方式：include 把公共步骤抽成被复用的用例，extend 描述可选或例外流程，泛化把共同部分抽象成父级，包括参与者泛化与用例泛化；每个关系都标出箭头方向与判断依据","\u002Fimages\u002Fsw-design-notes-10-include-extend.svg",[26,73,75],{"id":74},"_1-include把公共步骤抽出来","1 include：把公共步骤抽出来",[13,77,78,81,82,85],{},[21,79,80],{},"include 关系（include relationship）"," 表示：这个用例",[21,83,84],{},"每次执行时都会","用到另一个用例。",[87,88,93],"pre",{"className":89,"code":91,"language":92},[90],"language-text","派单      --include--> 校验工单状态\n申请配件  --include--> 校验工单状态\n回填工单  --include--> 校验工单状态\n","text",[94,95,91],"code",{"__ignoreMap":96},"",[98,99,101],"h3",{"id":100},"_11-它会每次都执行吗","1.1 它会每次都执行吗",[13,103,104,105],{},"判断标准就一条：",[21,106,107],{},"这个步骤会不会每次都被执行？",[13,109,110,111,114,115],{},"「校验工单状态」在这三个用例里都是必经的第一步 —— 工单不处于「进行中」，后面什么都不能做。所以适合 include。而被 include 的那个用例（图上叫 ",[94,112,113],{},"校验工单状态","）通常不是一个完整的目标，它更像是被复用的一个片段 —— 这是 include 的一个特征：",[21,116,117],{},"被 include 的用例一般不独立出现。",[98,119,121],{"id":120},"_12-箭头方向是唯一的坑","1.2 箭头方向是唯一的坑",[13,123,124,125,128],{},"include 的箭头",[21,126,127],{},"由基用例指向被包含用例","：",[87,130,133],{"className":131,"code":132,"language":92},[90],"派单 ──«include»──> 校验工单状态\n",[94,134,132],{"__ignoreMap":96},[13,136,137,138,141],{},"记法：",[21,139,140],{},"「我需要它」—— 箭头朝被需要的一方","。这个方向画反，语义就变成「校验工单状态依赖派单」，完全说不通。",[13,143,144,145,148],{},"还有一条讲义明确说的：",[21,146,147],{},"线用实线还是虚线都可以","。所以不要在线的样式上纠结，方向才是分。",[98,150,152],{"id":151},"_13-什么不该做成-include","1.3 什么不该做成 include",[13,154,155,156],{},"讲义里有一句直接的刹车条件：",[21,157,158],{},"不要给「只涉及一个屏幕」的东西做 include。",[13,160,161,162,165],{},"贯穿案例里的反例：有人会把「显示工单详情」做成 include。但「显示详情」只是一个界面动作，它每次都会被做，却不是一个可复用的",[21,163,164],{},"业务步骤","。把它抽出来只会让图变复杂，不会让任何东西变清楚。",[26,167,169],{"id":168},"_2-extend可选与例外","2 extend：可选与例外",[13,171,172,175,176,179],{},[21,173,174],{},"extend 关系（extend relationship）"," 表示：在特定条件下，主用例会",[21,177,178],{},"额外","做一件事。",[87,181,184],{"className":182,"code":183,"language":92},[90],"申请配件  \u003C--extend--  撤回申请（5 分钟内）\n申请配件  \u003C--extend--  转人工审核（金额超 500）\n",[94,185,183],{"__ignoreMap":96},[13,187,188,189],{},"判断标准：",[21,190,191],{},"这个步骤只在某些条件下发生，而主用例本身并不知道它存在。",[13,193,194,195],{},"最后半句是关键。主用例（base use case）不需要知道有「撤回」这回事 —— 「申请配件」该做的就是申请。撤回是叠加上去的可选项。",[21,196,197],{},"如果主用例必须知道对方的存在才能跑通，那就不是 extend，而是 include 或者主流程的一部分。",[98,199,201],{"id":200},"_21-扩展点挂在哪里","2.1 扩展点挂在哪里",[13,203,204],{},"这里有个衍生出来的「扩展点（extension point）」概念：扩展用例挂在主用例的哪个位置触发。",[13,206,207,208],{},"本例里「撤回申请」的扩展点是「提交成功之后、仓管出库之前」—— 因为一旦仓管已经出库，就不是撤回而是退货了。",[21,209,210],{},"扩展点写清楚，实现的边界就清楚了。",[98,212,214],{"id":213},"_22-箭头方向与-include-相反","2.2 箭头方向与 include 相反",[13,216,217],{},"这是全篇最集中的扣分点，一定要记成条件反射：",[87,219,222],{"className":220,"code":221,"language":92},[90],"撤回申请 ──«extend»──> 申请配件\n",[94,223,221],{"__ignoreMap":96},[13,225,226,229,230],{},[21,227,228],{},"扩展用例指向基用例","。记法与 include 对称：",[21,231,232],{},"「我附加到它」—— 箭头朝被附加的一方。",[13,234,235],{},"两个补充细节：",[37,237,238,244],{},[40,239,240,243],{},[21,241,242],{},"条件写在箭头旁，不加方括号。","（方括号是活动图 guard 的写法 —— 这个区别每年都有人混。）",[40,245,246],{},"讲义说 extend 的条件也可以不写。但写了更好，因为它说明了这条扩展什么时候触发。",[98,248,250],{"id":249},"_23-基用例必须自己站得住","2.3 基用例必须自己站得住",[13,252,253,254],{},"extend 有一个容易忽略的前提：",[21,255,256],{},"被扩展的那个用例，即使扩展不执行，也必须是一个完整、有意义的目标。",[13,258,259],{},"「申请配件」即使永远不撤回，它也是完整的。「撤回申请」单独拿出来呢？它没有意义 —— 你没申请过，撤什么？",[13,261,262,263,266],{},"这就引出一条判据：",[21,264,265],{},"如果一个用例单独看没有意义，它很可能是某个基用例的扩展，而不是独立用例","。这一条能帮你判断方向。",[26,268,270],{"id":269},"_3-include-和-extend-的区别一句话","3 include 和 extend 的区别，一句话",[272,273,274,289],"table",{},[275,276,277],"thead",{},[278,279,280,283,286],"tr",{},[281,282],"th",{},[281,284,285],{},"include",[281,287,288],{},"extend",[290,291,292,306,317,332,343,358],"tbody",{},[278,293,294,298,303],{},[295,296,297],"td",{},"执行时机",[295,299,300],{},[21,301,302],{},"每次都会执行",[295,304,305],{},"只在特定条件下执行",[278,307,308,311,314],{},[295,309,310],{},"依赖方向",[295,312,313],{},"主用例依赖被包含用例",[295,315,316],{},"扩展用例依赖主用例",[278,318,319,322,327],{},[295,320,321],{},"箭头方向",[295,323,324],{},[21,325,326],{},"基用例 → 被包含用例",[295,328,329],{},[21,330,331],{},"扩展用例 → 基用例",[278,333,334,337,340],{},[295,335,336],{},"主用例是否知道",[295,338,339],{},"知道，是它的必经步骤",[295,341,342],{},"不知道，是可叠加的行为",[278,344,345,348,351],{},[295,346,347],{},"能否独立存在",[295,349,350],{},"被包含用例一般不独立",[295,352,353,354,357],{},"扩展用例",[21,355,356],{},"不能","独立",[278,359,360,363,366],{},[295,361,362],{},"典型用途",[295,364,365],{},"复用公共步骤",[295,367,368],{},"可选功能、例外流程",[13,370,371,372,375],{},"记不住的时候，问一句：",[21,373,374],{},"「这一步能不做吗？」"," 不能不做 → include；可能不做 → extend。",[26,377,379],{"id":378},"_4-泛化把共同的部分提到父级","4 泛化：把共同的部分提到父级",[13,381,382,385],{},[21,383,384],{},"泛化（generalization）"," 是把多个元素共同的部分抽象成一个父级。用例模型里有两个地方能用到：",[98,387,389],{"id":388},"_41-参与者泛化","4.1 参与者泛化",[13,391,392,395],{},[21,393,394],{},"① 参与者泛化（generalization of actors）。"," 本例里就是「客服」和「片区经理」：两人共用一整套用例（看待派单、看排期、改约），区别只在经理多几个「看报表」的用例。",[13,397,398],{},"做法是把共同的放到父级「门店员工」，子级只画差异。",[87,400,403],{"className":401,"code":402,"language":92},[90],"       门店员工 (abstract)\n         △          △\n         │          │\n       客服       片区经理\n",[94,404,402],{"__ignoreMap":96},[13,406,407,408,411],{},"判断标准是 08 篇那条：",[21,409,410],{},"他们做的事一样吗","？一样（只是权限不同）→ 泛化或合并；不一样 → 保持两个独立角色。",[98,413,415],{"id":414},"_42-用例泛化","4.2 用例泛化",[13,417,418,421],{},[21,419,420],{},"② 用例泛化（generalization of use cases）。"," 本例里：「空调报修」「冰箱报修」「洗衣机报修」的流程主体一样（描述故障 → 选时段 → 提交），只有故障类型对应的表单字段不同。",[13,423,424],{},"把「提交报修」作为父用例（抽象用例，abstract use case，本身不直接执行），三个子用例继承并覆盖差异部分。",[13,426,427,428,431,432,435],{},"不过本例里我不建议这么做 —— 三种家电的差异只是表单字段，用",[21,429,430],{},"一个用例 + 一个「家电类型」字段","就够了。用例泛化只在",[21,433,434],{},"流程本身不同","时才值得用。",[98,437,439],{"id":438},"_43-箭头方向","4.3 箭头方向",[13,441,442,443,446],{},"泛化的箭头是",[21,444,445],{},"空心三角，由子指向父","。同一张图上如果既有 include\u002Fextend 又有泛化，最容易混的就是这三种箭头。",[13,448,137,449],{},[21,450,451],{},"空心三角是「继承」，永远指向父；虚线开放箭头是「引用」，指向被引用方。",[98,453,455],{"id":454},"_44-收益与代价","4.4 收益与代价",[13,457,458,459,462],{},"泛化的价值在于",[21,460,461],{},"改动集中","：共同流程变一次，所有子级都跟着变。代价是抽象层次变多，读图的人要多跳一层。所以只有当「差异确实很小、共同部分确实很多」时才值得用。",[26,464,466],{"id":465},"_5-精炼到什么程度就该停","5 精炼到什么程度就该停",[13,468,469],{},"精炼有一个明显的收益递减点。把每个用例都拆成 include、再给每个角色都建一个父级，最后得到一张谁也看不懂的图 —— 这就是过度抽象（over-abstraction）。",[13,471,472],{},"讲义里给了几条具体的刹车条件：",[37,474,475,481,487],{},[40,476,477,480],{},[21,478,479],{},"不要为「只涉及一个屏幕」的行为建 include\u002Fextend","。判据是：两个界面看起来很像，可能只是一个在显示模式、一个在编辑模式 —— 那不是共同流程，是两种不同的任务；",[40,482,483,486],{},[21,484,485],{},"include 的嵌套不要超过一层","。被包含的用例不要再 include 别的东西，否则读图要跳三层；",[40,488,489],{},[21,490,491],{},"extend 的嵌套也不要超过一层。",[13,493,494,495,498],{},"一条总原则：",[21,496,497],{},"如果精炼之后，业务方看图的难度上升了，那这次精炼就是不值得的","。用例模型的首要读者是业务方，不是建模的人。",[13,500,501],{},"另外，用例变多之后的分组（use case package）是另一层组织手段，笔记 08 已经讲过。",[26,503,505],{"id":504},"_6-先细化哪一个两种替代方案","6 先细化哪一个：两种替代方案",[13,507,508],{},"最后剩一个实际问题：用例很多，先细化哪个？两种常见策略：",[98,510,512],{"id":511},"_61-策略一先做主线","6.1 策略一：先做主线",[13,514,515,518],{},[21,516,517],{},"策略一：先做主线。"," 先把核心流程（派单、申请配件）细化到能开发，再处理分支。适合要尽早看到可用版本的场景。",[98,520,522],{"id":521},"_62-策略二先做分支","6.2 策略二：先做分支",[13,524,525,528],{},[21,526,527],{},"策略二：先做分支。"," 先把例外流程（缺料、改约、退回）理清。适合业务规则复杂、例外很多、返工成本高的场景。",[98,530,532],{"id":531},"_63-怎么选","6.3 怎么选",[13,534,535,536,539],{},"选哪种取决于",[21,537,538],{},"哪一类问题更容易推翻已有的设计","。如果例外流程经常改主线，那就该先做分支 —— 因为先做的主线很可能白做。",[13,541,542,543,546],{},"本例恰好属于后者：",[21,544,545],{},"「缺料」这个例外几乎改写了整个主流程","（工单要转「待件挂起」、客户要改约、仓管要催货）。如果先做主线再发现这一点，主线要返工。所以本例该先做分支。",[26,548,549],{"id":549},"本节自测",[551,552,553,556,559,562,565,568],"ol",{},[40,554,555],{},"判断用 include 还是 extend，只需要问哪一个问题？",[40,557,558],{},"「派单」需要知道「撤回申请」的存在吗？这个问题的答案说明了什么？",[40,560,561],{},"include 和 extend 的箭头方向分别指向哪一方？各用什么记法记住？",[40,563,564],{},"参与者泛化和用例泛化分别在什么情况下有用？本例中哪个建议用、哪个不建议？",[40,566,567],{},"精炼到什么程度就该停下？讲义给了哪三条具体的刹车条件？",[40,569,570],{},"什么情况下应该先细化例外流程，而不是主线？本例属于哪种？",[98,572,573],{"id":573},"参考答案",[551,575,576,581,588,598,612,619],{},[40,577,578,580],{},[21,579,374],{}," 不能不做 → include；可能不做 → extend（§3）。",[40,582,583,584,587],{},"不需要 —— 「派单」该做的就是派单，撤回是叠加在「申请配件」上的可选项。这说明了",[21,585,586],{},"主用例不必知道扩展用例的存在","；反之若主用例必须知道对方才能跑通，那就不是 extend 而是 include（§2、§3）。",[40,589,590,593,594,597],{},[21,591,592],{},"include：基用例 → 被包含用例","，记法「我需要它」；",[21,595,596],{},"extend：扩展用例 → 基用例","，记法「我附加到它」（§1.2、§2.2）。",[40,599,600,603,604,607,608,611],{},[21,601,602],{},"参与者泛化","用于两人共用一整套用例、只是权限不同的情况 —— 本例「客服」与「片区经理」建议用；",[21,605,606],{},"用例泛化","用于流程本身不同、共同部分很多的情况 —— 本例三种家电报修只是表单字段不同，",[21,609,610],{},"不建议","用，一个用例加一个「家电类型」字段就够（§4.1、§4.2）。",[40,613,614,615,618],{},"停的判据是 ",[21,616,617],{},"「业务方看图的难度上升了就不值得」","。三条具体刹车：不要为只涉及一个屏幕的行为建 include\u002Fextend；include 嵌套不超过一层；extend 嵌套不超过一层（§5）。",[40,620,621,622,625],{},"当",[21,623,624],{},"例外流程容易推翻主线设计","时该先做分支。本例属于这种 —— 「缺料」这个例外几乎改写了主流程（工单转待件挂起、客户改约、仓管催货），先做主线会返工（§6.3）。",[26,627,628],{"id":628},"术语表",[272,630,631,644],{},[275,632,633],{},[278,634,635,638,641],{},[281,636,637],{},"术语",[281,639,640],{},"中文",[281,642,643],{},"一句话解释",[290,645,646,657,668,679,690,700,710,721,732,743],{},[278,647,648,651,654],{},[295,649,650],{},"include relationship",[295,652,653],{},"包含关系",[295,655,656],{},"主用例每次执行都会用到的公共步骤",[278,658,659,662,665],{},[295,660,661],{},"extend relationship",[295,663,664],{},"扩展关系",[295,666,667],{},"只在特定条件下叠加到主用例上的行为",[278,669,670,673,676],{},[295,671,672],{},"base use case",[295,674,675],{},"基础用例",[295,677,678],{},"被扩展的那个主用例",[278,680,681,684,687],{},[295,682,683],{},"extension point",[295,685,686],{},"扩展点",[295,688,689],{},"扩展用例挂载到主用例上的触发位置",[278,691,692,695,697],{},[295,693,694],{},"generalization of actors",[295,696,602],{},[295,698,699],{},"把多个角色的共同部分抽象成父角色",[278,701,702,705,707],{},[295,703,704],{},"generalization of use cases",[295,706,606],{},[295,708,709],{},"把多个用例的共同流程抽象成父用例",[278,711,712,715,718],{},[295,713,714],{},"abstract use case",[295,716,717],{},"抽象用例",[295,719,720],{},"只用于被继承、本身不直接执行的用例",[278,722,723,726,729],{},[295,724,725],{},"refining the use case model",[295,727,728],{},"精炼用例模型",[295,730,731],{},"用 include、extend、泛化整理用例结构",[278,733,734,737,740],{},[295,735,736],{},"use case package",[295,738,739],{},"用例包",[295,741,742],{},"按主题、角色或阶段对用例分组",[278,744,745,748,751],{},[295,746,747],{},"over-abstraction",[295,749,750],{},"过度抽象",[295,752,753],{},"抽象层次过多，反而降低可读性",{"title":96,"searchDepth":755,"depth":755,"links":756},3,[757,759,764,769,770,776,777,782,785],{"id":28,"depth":758,"text":29},2,{"id":74,"depth":758,"text":75,"children":760},[761,762,763],{"id":100,"depth":755,"text":101},{"id":120,"depth":755,"text":121},{"id":151,"depth":755,"text":152},{"id":168,"depth":758,"text":169,"children":765},[766,767,768],{"id":200,"depth":755,"text":201},{"id":213,"depth":755,"text":214},{"id":249,"depth":755,"text":250},{"id":269,"depth":758,"text":270},{"id":378,"depth":758,"text":379,"children":771},[772,773,774,775],{"id":388,"depth":755,"text":389},{"id":414,"depth":755,"text":415},{"id":438,"depth":755,"text":439},{"id":454,"depth":755,"text":455},{"id":465,"depth":758,"text":466},{"id":504,"depth":758,"text":505,"children":778},[779,780,781],{"id":511,"depth":755,"text":512},{"id":521,"depth":755,"text":522},{"id":531,"depth":755,"text":532},{"id":549,"depth":758,"text":549,"children":783},[784],{"id":573,"depth":755,"text":573},{"id":628,"depth":758,"text":628},"学习",null,"2026-11-01","三种精炼用例关系的判断标准：include 什么时候用、它和 extend 的区别到底在哪、参与者泛化与用例泛化各解决什么问题，以及精炼到什么程度就该停手。","md",false,{},true,"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-10",{"title":5,"description":789},"软件分析与设计笔记",10,"posts\u002FswDesign\u002Fsw-design-notes-10",[800,801,802,803],"需求分析","用例建模","UML","学习笔记","d3Z9buHuoUgYCIqTU3C7qi7B9VEhy0V4qZ7SE_zRN7E",[806,812,818,824,830,836,842,848,854,860,866,872,878,884,890,896,902,908,914,915,921,927,933,939,945,951,956,961,967,973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1023,1027,1033,1037,1041,1045,1049,1053,1057,1061,1066,1070,1074,1079,1084,1090,1094,1099,1103],{"path":807,"title":808,"description":809,"date":810,"series":796,"seriesOrder":811},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-28","软件分析与设计笔记 28：draw.io 作图手册：五张图的符号位置、落笔顺序与自检","不模拟软件界面，只讲清三件事：每个 UML 符号在 draw.io 里搜什么词、一张图该按什么顺序落笔、画完拿什么标准自查。覆盖用例图、活动图、顺序图、类图、界面草图五种图纸，末尾附「最容易画反的十二笔」总检表。","2026-11-19",28,{"path":813,"title":814,"description":815,"date":816,"series":796,"seriesOrder":817},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-27","软件分析与设计笔记 27：Paper 1 应试：从读案例到交卷的一次完整演算","拿一份完整的案例，从头到尾走一遍：怎么读案例列角色、怎么在四个小问之间分配时间、每一张图从哪一句话推出来、两张表怎么填、交卷前拿什么清单自查。附 C25 真题与样卷的题型分值对照。","2026-11-18",27,{"path":819,"title":820,"description":821,"date":822,"series":796,"seriesOrder":823},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-26","软件分析与设计笔记 26：总结：把整条链路串起来","系列收尾：从需求到代码的完整链路回顾、每一环的产出与验收方式、六条贯穿全系列的原则，以及一份可以直接拿去做项目自查的清单。","2026-11-17",26,{"path":825,"title":826,"description":827,"date":828,"series":796,"seriesOrder":829},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-25","软件分析与设计笔记 25：代码重构：坏味道与手法","重构不改变外部行为、只改善内部结构：四种常见坏味道与对应手法、为什么动手之前必须先有测试、什么时候该重构，以及重构和前面那些设计模型是什么关系。","2026-11-16",25,{"path":831,"title":832,"description":833,"date":834,"series":796,"seriesOrder":835},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-24","软件分析与设计笔记 24：设计原则：内聚、耦合与多态","好设计怎么衡量：内聚与耦合分别在说什么、一次从「差设计」到「多态重设计」的完整对照、抽象类与接口的选择依据，以及复用为什么会从优势变成风险。","2026-11-15",24,{"path":837,"title":838,"description":839,"date":840,"series":796,"seriesOrder":841},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-23","软件分析与设计笔记 23：设计建模：从对象模型到能实现的方案","分析模型能用但还不能直接开工：对象模型怎么映射到关系模型、继承映射的三种方案怎么选、数据访问层与 Data Broker 解决什么问题，以及三层架构与 MVC 的分工。","2026-11-14",23,{"path":843,"title":844,"description":845,"date":846,"series":796,"seriesOrder":847},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-22","软件分析与设计笔记 22：分析建模：边界、实体与控制对象","不经过分析直接写代码会踩什么坑、三种分析对象各管一段、怎么用顺序图完成一次用例实现，以及从顺序图导出类的属性、操作与关系的方法。","2026-11-13",22,{"path":849,"title":850,"description":851,"date":852,"series":796,"seriesOrder":853},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-21","软件分析与设计笔记 21：UML：类图与顺序图","为什么需要一种共同的画法：类符号三格怎么画、五种关系的符号与设计含义、每种关系落到 Java 是什么写法，顺序图的三要素怎么摆；以及这三种最容易找不到的符号在 draw.io 里各搜什么词。","2026-11-12",21,{"path":855,"title":856,"description":857,"date":858,"series":796,"seriesOrder":859},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-20","软件分析与设计笔记 20：面向对象概念：对象、关系与多态","面向对象不是「用某个语言写代码」：对象与类的区别、封装真正在保护什么、五种关系（依赖、关联、聚合、组合、继承）各自何时用，以及多态为什么能减少条件判断。","2026-11-11",20,{"path":861,"title":862,"description":863,"date":864,"series":796,"seriesOrder":865},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-19","软件分析与设计笔记 19：规范化与逻辑数据模型","一次改地址引发的数据不一致，讲清规范化的真正动机；UNF 到 3NF 每一步在解决什么问题，派生字段为什么不能存，优化为什么和规范化方向相反，以及逻辑数据模型在 3NF 之上补了什么。","2026-11-10",19,{"path":867,"title":868,"description":869,"date":870,"series":796,"seriesOrder":871},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-18","软件分析与设计笔记 18：数据库分析：实体、关系与数据字典","为什么不能拿到需求就动手建表：实体与关系怎么判定、基数为什么必须由业务方确认、数据命名要遵守什么规则，以及数据字典四列里最容易被空着的那一列。","2026-11-09",18,{"path":873,"title":874,"description":875,"date":876,"series":796,"seriesOrder":877},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-17","软件分析与设计笔记 17：用生成式 AI 做设计与开发：能用的场景与要防的坑","生成式 AI 在工作流里真正站得住的三个场景 —— 造示例数据、解释陌生代码、起草初稿；它为什么会复现社会偏见、为什么会编得像真的，以及哪些内容绝不能直接采用。","2026-11-08",17,{"path":879,"title":880,"description":881,"date":882,"series":796,"seriesOrder":883},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-16","软件分析与设计笔记 16：从 persona 到 URS：一份交付物的完整骨架","把前面十几篇串成一份能交出去的东西：用户需求规格说明书的九个部分、每一章来自哪个环节、由谁确认，以及面向决策者的提案该讲清哪三件事。","2026-11-07",16,{"path":885,"title":886,"description":887,"date":888,"series":796,"seriesOrder":889},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-15","软件分析与设计笔记 15：非功能需求：把「快、稳、安全」变成数字","为什么「系统要快」不是一条需求：非功能需求怎么量化，决定性能的用户量、交易量、数据量三张表怎么估，安全需求要写清什么，以及可靠性的「不可用」该怎么定义。","2026-11-06",15,{"path":891,"title":892,"description":893,"date":894,"series":796,"seriesOrder":895},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-14","软件分析与设计笔记 14：软件测试：层级、方法与等价类划分","「我们测过了」这句话问题在哪：为什么静态评审和动态测试缺一不可，五个测试层级各自验证什么，白盒与黑盒的差别，以及用等价类划分和边界值把无限输入变成有限用例的方法。","2026-11-05",14,{"path":897,"title":898,"description":899,"date":900,"series":796,"seriesOrder":901},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-13","软件分析与设计笔记 13：需求获取：五种调查方法与一场访谈","需求从哪里来：五种事实调查方法各自适合回答什么、代价是什么，访谈从准备到记录的全过程，问卷设计里最容易犯的错，以及访谈记录必须写下来的那几项信息。","2026-11-04",13,{"path":903,"title":904,"description":905,"date":906,"series":796,"seriesOrder":907},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-12","软件分析与设计笔记 12：前端设计：把流程变成屏幕","界面设计不是「画得好看」：从活动图走到屏幕的三步，CRUD 任务流怎么当检查表，界面必须回答的三个问题，信息架构与五种导航类型，视觉一致性与留白，以及移动端与 Web 端的差别。","2026-11-03",12,{"path":909,"title":910,"description":911,"date":912,"series":796,"seriesOrder":913},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-11","软件分析与设计笔记 11：Agentic AI 与业务流程自动化","AI agent 到底是什么：五项能力与四个核心组件，什么场景该用定时任务、什么该用程序、什么才值得用 agent，两种实现路径的差别，必须提前防住的四类失败模式，以及设计方法从提示词到编排的演进。","2026-11-02",11,{"path":794,"title":5,"description":789,"date":788,"series":796,"seriesOrder":797},{"path":916,"title":917,"description":918,"date":919,"series":796,"seriesOrder":920},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-09","软件分析与设计笔记 09：功能需求（二）：从流程到业务实体","活动图画完之后，怎么从流程里筛出真正的业务实体：三条判断标准、哪些名词其实不该建实体、属性从哪里来，以及领域模型和后面的数据模型分别负责什么。","2026-10-31",9,{"path":922,"title":923,"description":924,"date":925,"series":796,"seriesOrder":926},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-08","软件分析与设计笔记 08：功能需求（一）：用例模型与活动图","从用户故事到用例的翻译过程：什么才算「好」的参与者，系统边界怎么划，用例描述必须写清哪四件事，活动图怎么把分支、并行与泳道画清楚，以及这两个图在 draw.io 里各用哪个形状、最容易画错哪一笔。","2026-10-30",8,{"path":928,"title":929,"description":930,"date":931,"series":796,"seriesOrder":932},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-07","软件分析与设计笔记 07：用户故事：把需求写成可验收的一句话","用户故事的三段结构，怎么从用户画像和数据地图里把它提取出来，一条故事多大算合适、时序为什么也是需求的一部分，以及验收标准怎么写才算真的可验收。","2026-10-29",7,{"path":934,"title":935,"description":936,"date":937,"series":796,"seriesOrder":938},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-06","软件分析与设计笔记 06：范围界定（数据侧）：用数据校验你的判断","访谈结论和数字对不上时该怎么办：分析的四个层次分别在回答什么问题、为什么不能跳级、数据准备与搜集怎么做成一张应用地图，以及数据最容易在哪几个地方骗你。","2026-10-28",6,{"path":940,"title":941,"description":942,"date":943,"series":796,"seriesOrder":944},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-05","软件分析与设计笔记 05：范围界定（用户侧）：用户画像与客户旅程图","为什么「所有用户」是一个陷阱，项目边界该怎么划，用户画像要写哪五个要素、最容易写成什么废纸，客户旅程图怎么把阶段、想法和情绪画在同一张纸上，以及怎么从这两样东西一步步推到可验证的需求。","2026-10-27",5,{"path":946,"title":947,"description":948,"date":949,"series":796,"seriesOrder":950},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-04","软件分析与设计笔记 04：接手陌生案例的第一步：三问与一张表","拿到一个完全陌生的业务案例，怎么从零开始不跑偏：只问 Who、What、Why 三个问题，把「问题—事实—假设—理由」记成一张四列表，说清为什么假设必须写明理由，以及带着问题进访谈为什么比空手去有效得多。","2026-10-26",4,{"path":952,"title":953,"description":954,"date":955,"series":796,"seriesOrder":755},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-03","软件分析与设计笔记 03：Agile 与 Scrum：迭代、角色与产出物","从「三周做完交付，客户说这不是我要的」讲起，说清瀑布式与迭代式的根本差别，Scrum 的三个角色和三种产出物，一个 sprint 里四次固定活动各自在解决什么问题，以及敏捷里最常见的几个误解。","2026-10-25",{"path":957,"title":958,"description":959,"date":960,"series":796,"seriesOrder":758},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-02","软件分析与设计笔记 02：设计思维、用户驱动与数据驱动","从「客服要一个能一次看完所有排期的界面」这个翻车案例讲起，说清设计思维到底是什么、用户驱动与数据驱动各自能看见什么又看不见什么、为什么真正的落点是把两者合起来的 data-informed design，以及访谈中怎么问出有用的信息。","2026-10-24",{"path":962,"title":963,"description":964,"date":965,"series":796,"seriesOrder":966},"\u002Fposts\u002Fswdesign\u002Fsw-design-notes-01","软件分析与设计笔记 01：课程地图与从需求到设计的主干","这一篇不写具体方法，先把整条链路铺开：软件分析与设计到底要解决什么问题，从需求到设计分成哪两段，中间有哪十几个环节，一份完整的交付物里应该有什么，以及后面 27 篇笔记怎么排、新人该按什么顺序读。","2026-10-23",1,{"path":968,"title":969,"description":970,"date":971,"series":972,"seriesOrder":966},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-01","Spring 学习笔记 01：IoC、依赖注入与第一个 Spring Boot 应用","从「手写 new 出来的三层结构」的痛点讲起，说清 IoC 容器、Bean 与依赖注入是怎么回事，再用 Maven 与 Spring Initializr 跑起第一个能返回 JSON 的应用。","2026-10-15","Spring 学习笔记",{"path":974,"title":975,"description":976,"date":971,"series":972,"seriesOrder":758},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-02","Spring 学习笔记 02：Spring Data JPA 基础——实体映射与 Repository","用 ORM 把类映射到表：@Entity、@Id、@GeneratedValue 怎么用，CrudRepository 三层接口怎么选，H2 与 MySQL 怎么配，以及 ddl-auto 到底该写哪个值。",{"path":978,"title":979,"description":980,"date":971,"series":972,"seriesOrder":755},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-03","Spring 学习笔记 03：JPA 查询——派生方法、JPQL 与分页排序","Repository 自带的方法只够按主键查，这篇讲三种自己造查询的方式：方法名派生、@Query 写 JPQL、原生 SQL，以及 Pageable 分页排序和投影返回。",{"path":982,"title":983,"description":984,"date":971,"series":972,"seriesOrder":950},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-04","Spring 学习笔记 04：JPA 关联映射与仓储测试","一对多、多对多怎么映射，谁负责维护外键，FetchType 与 CascadeType 该怎么选，顺带用 @DataJpaTest 与 AssertJ 把仓储层测起来。",{"path":986,"title":987,"description":988,"date":971,"series":972,"seriesOrder":944},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-05","Spring 学习笔记 05：Spring MVC 与 Thymeleaf 入门","一次页面请求在 Spring MVC 里走了哪几步，@Controller 怎么把数据交给模板，@PathVariable 与 @RequestParam 怎么取参数，以及控制器为什么必须是无状态的。",{"path":990,"title":991,"description":992,"date":971,"series":972,"seriesOrder":938},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-06","Spring 学习笔记 06：表单绑定与服务端校验","把表单字段绑到对象上，用 Bean Validation 注解做服务端校验，把错误信息显示回用户刚填的那一页，再补上自定义校验注解与全局异常处理。",{"path":994,"title":995,"description":996,"date":971,"series":972,"seriesOrder":932},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-07","Spring 学习笔记 07：Thymeleaf 片段复用与国际化","用 th:fragment 与 th:insert\u002Fth:replace 把导航栏、页脚抽成片段，页面套用统一布局，再用 messages.properties 与 LocaleResolver 做中英文切换。",{"path":998,"title":999,"description":1000,"date":971,"series":972,"seriesOrder":926},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-08","Spring 学习笔记 08：HTTP Session 与会话状态管理","HTTP 无状态带来的问题，Cookie 与 Session 怎么分工，Spring 里读写会话的几种方式，以及会话固定、超时、多实例共享这些必须想清楚的事。",{"path":1002,"title":1003,"description":1004,"date":971,"series":972,"seriesOrder":920},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-09","Spring 学习笔记 09：拦截器 HandlerInterceptor","登录检查、权限判断、请求耗时统计这类横切逻辑该放哪：HandlerInterceptor 的三个回调各在什么时机执行，怎么注册与排除路径，以及它和过滤器的分工。",{"path":1006,"title":1007,"description":1008,"date":971,"series":972,"seriesOrder":797},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-10","Spring 学习笔记 10：Service 层与事务","一组数据库操作要么全成功要么全失败，靠 @Transactional 怎么做到；传播行为、隔离级别、回滚规则怎么选，以及自调用失效、乐观锁悲观锁这些实战里的坑。",{"path":1010,"title":1011,"description":1012,"date":971,"series":972,"seriesOrder":913},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-11","Spring 学习笔记 11：REST API 基础——把 Service 暴露成接口","同一套 Service 怎么同时服务页面和客户端：@RestController 的路由与方法语义、状态码怎么给、DTO 怎么定，以及 MockMvc 端到端和切片测试的两种写法与各自的坑。",{"path":1014,"title":1015,"description":1016,"date":971,"series":972,"seriesOrder":907},"\u002Fposts\u002Fjavaspring\u002Fspring-notes-12","Spring 学习笔记 12：REST 进阶——版本、更新、跨域与文档化","接口能跑通之后要处理的几件事：URL 与响应结构怎么定、版本怎么演进、更新怎么做到可安全重试、CORS 怎么配、HATEOAS 与 Spring Data REST 该不该用。",{"path":1018,"title":1019,"description":1020,"date":1021,"series":1022,"seriesOrder":950},"\u002Fposts\u002Fjava\u002Fjava-functionalprogramming-notes","Java 函数式编程笔记：Lambda、函数式接口与 Streams","学习 Java 函数式编程时的个人笔记：Lambda 表达式的五步简写与方法引用、常见函数式接口、Stream 流水线与惰性求值，以及创建流、中间操作、终止操作与收集器的用法。示例代码均为自写，运行结果来自本地实测。","2026-10-13","Java 学习笔记",{"path":1024,"title":1025,"description":1026,"date":1021,"series":1022,"seriesOrder":755},"\u002Fposts\u002Fjava\u002Fjava-generics-notes","Java 泛型笔记：泛型方法、有界类型与泛型类","学习 Java 泛型时的个人笔记：从重复的重载方法与多态尝试讲起，再到泛型方法与类型推断、有界泛型类型、Comparable 与 Number 边界，以及泛型类在集合中的用法。示例代码均为自写，运行结果来自本地实测。",{"path":1028,"title":1029,"description":1030,"date":1031,"series":1032,"seriesOrder":950},"\u002Fposts\u002Fmysql\u002Fmysql-learning-04","MySQL 学习笔记 04：数据操纵（增删改）","MySQL 学习笔记第四篇：INSERT 插入（含批量插入与 INSERT ... SELECT）、UPDATE 更新、DELETE 删除，以及各自的坑。","2026-10-10","SQL 学习笔记",{"path":1034,"title":1035,"description":1036,"date":1031,"series":1032,"seriesOrder":944},"\u002Fposts\u002Fmysql\u002Fmysql-learning-05","MySQL 学习笔记 05：DDL 概览","MySQL 学习笔记第五篇：数据定义语言概览——Schema、Table、View、Index、Constraints 这几类对象分别对应哪些 CREATE \u002F ALTER \u002F DROP 命令。",{"path":1038,"title":1039,"description":1040,"date":1031,"series":1032,"seriesOrder":938},"\u002Fposts\u002Fmysql\u002Fmysql-learning-06","MySQL 学习笔记 06：CREATE TABLE 与数据类型","MySQL 学习笔记第六篇：CREATE TABLE 的完整语法、常用数据类型的选型建议，以及外键的两种定义方式。",{"path":1042,"title":1043,"description":1044,"date":1031,"series":1032,"seriesOrder":932},"\u002Fposts\u002Fmysql\u002Fmysql-learning-07","MySQL 学习笔记 07：索引","MySQL 学习笔记第七篇：索引的作用与 B+ 树结构、单列与复合索引的创建、索引的代价与适用场景。",{"path":1046,"title":1047,"description":1048,"date":1031,"series":1032,"seriesOrder":926},"\u002Fposts\u002Fmysql\u002Fmysql-learning-08","MySQL 学习笔记 08：DROP 与 ALTER","MySQL 学习笔记第八篇：DROP 删除数据库对象、ALTER 修改表结构，以及常见的坑。",{"path":1050,"title":1051,"description":1052,"date":1031,"series":1032,"seriesOrder":920},"\u002Fposts\u002Fmysql\u002Fmysql-learning-09","MySQL 学习笔记 09：约束与数据完整性","MySQL 学习笔记第九篇：SQL 约束——NOT NULL、CHECK、PRIMARY KEY、FOREIGN KEY 等如何保证数据完整性，以及它们的对比。",{"path":1054,"title":1055,"description":1056,"date":1031,"series":1032,"seriesOrder":797},"\u002Fposts\u002Fmysql\u002Fmysql-learning-10","MySQL 学习笔记 10：视图","MySQL 学习笔记第十篇：视图（Views）的定义与特性、和表的对比、创建与使用，以及可更新视图的限制。",{"path":1058,"title":1059,"description":1060,"date":1031,"series":1032,"seriesOrder":913},"\u002Fposts\u002Fmysql\u002Fmysql-learning-11","MySQL 学习笔记 11：存储过程","MySQL 学习笔记第十一篇：存储过程（Stored Procedures）的概念、创建与调用、带参数的写法，以及优缺点。",{"path":1062,"title":1063,"description":1064,"date":1065,"series":1032,"seriesOrder":966},"\u002Fposts\u002Fmysql\u002Fmysql-learning-01","MySQL 学习笔记 01：MySQL 基础知识","MySQL 学习笔记第一篇：MySQL Server 与 Workbench 的安装，数据库与 DBMS 的基本概念，关系模型、ACID 事务、常见数据类型，以及后续的学习路线。","2026-10-09",{"path":1067,"title":1068,"description":1069,"date":1065,"series":1032,"seriesOrder":758},"\u002Fposts\u002Fmysql\u002Fmysql-learning-02","MySQL 学习笔记 02：SELECT 查询与过滤","MySQL 学习笔记第二篇：SELECT 的基本语法与逻辑执行顺序，WHERE 过滤（比较、LIKE、IN、BETWEEN、NULL）、各种 JOIN、UNION 与子查询。",{"path":1071,"title":1072,"description":1073,"date":1065,"series":1032,"seriesOrder":755},"\u002Fposts\u002Fmysql\u002Fmysql-learning-03","MySQL 学习笔记 03：聚合、分组与输出控制","MySQL 学习笔记第三篇：COUNT\u002FSUM\u002FAVG 等聚合函数、GROUP BY 分组、HAVING 过滤、ORDER BY 与 LIMIT，以及一条 SELECT 的完整逻辑执行顺序。",{"path":1075,"title":1076,"description":1077,"date":1078,"series":1022,"seriesOrder":758},"\u002Fposts\u002Fjava\u002Fjava-oop-notes","Java 面向对象笔记：类、继承、多态与异常","学习 Java 面向对象编程时的个人笔记：类与对象、封装与访问修饰符、继承与多态、集合、抽象类与接口、异常处理。示例代码均为自写，运行结果来自本地实测。","2026-10-06",{"path":1080,"title":1081,"description":1082,"date":1083,"series":1022,"seriesOrder":966},"\u002Fposts\u002Fjava\u002Fjava-basics-notes","Java 基础语法笔记：从环境安装到方法与数组","学习 Java 基础语法时的个人笔记：JDK 安装、输入输出、变量与运算符、条件与循环、字符串、数组与方法。示例代码均为自写，运行结果来自本地实测。","2026-10-05",{"path":1085,"title":1086,"description":1087,"date":1088,"series":1089,"seriesOrder":966},"\u002Fposts\u002Fgit\u002Fgit-install-and-basics","Git 安装与基本使用","从装好 Git 到跑通日常提交流程，把最常用的几条命令和它们背后的三个区域讲清楚。","2026-10-04","Git 入门",{"path":1091,"title":1092,"description":1093,"date":1088,"series":1089,"seriesOrder":758},"\u002Fposts\u002Fgit\u002Flocal-remote-repository","本地远程仓库的部署","用自己的机器当 Git 服务器：建一个裸仓库、走 SSH 连上去，把本地提交推上去。",{"path":1095,"title":1096,"description":1097,"date":1088,"series":1098,"seriesOrder":966},"\u002Fposts\u002Fguide\u002Fguide-file-layout","文件都放在哪","文章、图片、封面分别放在仓库的什么位置，哪些目录可以放手改、哪些碰了会出问题。","博客使用指南",{"path":1100,"title":1101,"description":1102,"date":1088,"series":1098,"seriesOrder":758},"\u002Fposts\u002Fguide\u002Fguide-frontmatter","frontmatter 每个字段是什么意思","文章开头那段被横线包住的字段，逐个说明作用、默认行为和写错的后果。",{"path":1104,"title":1105,"description":1106,"date":1088,"series":1098,"seriesOrder":755},"\u002Fposts\u002Fguide\u002Fguide-markdown","正文怎么写 —— Markdown 与排版效果","标题、链接、图片、代码、表格分别怎么写，以及本站对它们做了哪些自定义处理。",1793268185121]