设计模式复习笔记:从可维护性到OOP原则与场景应用
复习到“面向可维护性的设计模式”这一块很多同学的第一反应是把二十多个模式的意图、结构图、优缺点全背下来。我当年也这么干过结果一到场景题题目换个说法就懵了不知道到底该用哪个模式。后来我把思路彻底换了一遍——先搞清楚“可维护性”这三个字到底在说什么再回头看这些模式一切就都顺了。这篇复习笔记就是按这个思路整理的。它不是什么官方教材的浓缩版而是我在复习和实际写代码过程中总结出来的理解方式每个模式解决什么问题、为什么它能提升可维护性、考试和面试里怎么识别场景。适合正在复习软件构造、准备期末考或者面试前突击设计模式的同学也适合写了两三年业务代码但总觉得“一改需求就牵一发动全身”的朋友。1. 先搞明白可维护性到底在维护什么1.1 三个字背后的真实成本可维护性Maintainability不是“代码能跑就行”的反义词它说的是当需求发生变化时你需要改动多少代码、改动会不会引入新bug、新同学接手时需要多久才能看懂。我见过太多项目功能一切正常但每次加需求就跟拆炸弹一样。改一个支付逻辑牵扯到订单状态、库存、通知、对账改完还得回归测试半天。这种系统的“可维护性”几乎为零。反过来说一个可维护性好的系统新需求来的时候你通常只需要在一个地方做修改或者新增一个类、一个模块就搞定了其他地方完全不用动。设计模式之所以重要就是因为它给了我们一套经过验证的“改动隔离”方案。每个模式都对应着一种“变化的轴”——有的是对象创建方式在变有的是算法在变有的是对象间的交互在变。模式把这些变化点封装起来让变化不扩散到整个系统。1.2 六大原则模式的灵魂模式是招式原则是内功。考试里经常让你“指出某模式体现了哪些原则”所以这六个原则一定得背熟而且要能说出“为什么”。SRP 单一职责原则一个类只因为一个原因而变化。这是最基础的一条也是被违反得最多的一条。OCP 开闭原则对扩展开放对修改关闭。可维护性的核心目标大多数模式都在为它服务。LSP 里氏替换原则子类必须能替换父类且行为不破坏。继承用得不对维护就是噩梦。ISP 接口隔离原则客户端不该依赖它不需要的接口。胖接口是耦合的温床。DIP 依赖倒置原则依赖抽象不依赖具体实现。这是解耦的钥匙。LOD 最少知识原则迪米特法则一个对象应该尽量少地知道其他对象的内部细节。也叫“不和陌生人说话”。把这六条理解了你会发现设计模式本质上就是这些原则在不同场景下的具体落地。比如策略模式落地了OCP和DIP观察者模式落地了OCP和最少知识原则工厂模式落地了DIP和SRP。提示考试答题时问到“为什么这个模式能提升可维护性”别只背模式的官方定义往原则上靠。能说出“通过引入抽象层隔离变化满足开闭原则”这种话得分明显不一样。2. 创建型模式对象从哪来决定了后面好不好改2.1 工厂方法 vs 抽象工厂别搞混场景完全不同工厂方法Factory Method的核心是定义一个创建对象的接口让子类决定实例化哪个类。它把一个类“要用什么具体产品”的决定权从调用方转移到了子类里。public interface Parser { Document parse(String content); } public abstract class DocumentParser { public Document parseFile(String path) { String content readFile(path); Parser parser createParser(); return parser.parse(content); } // 子类决定用哪个Parser protected abstract Parser createParser(); }这段代码里parseFile的流程是固定的但createParser是个“钩子”子类可以返回不同的解析器。以后要支持新格式新增一个子类就行不用动公共流程。抽象工厂Abstract Factory则是“生产一族相关产品”的工厂。比如一个UI框架要支持深色主题和浅色主题每套主题都有按钮、输入框、弹窗这时候用一个ThemeFactory让DarkThemeFactory和LightThemeFactory分别生产配套的组件。区别很简单工厂方法针对“一个产品等级结构”抽象工厂针对“多个产品等级结构的组合”。考试常考的是给你一个场景让你判断该用哪个——看到“多个产品族”就是抽象工厂看到“一个产品有多种实现且创建流程固定”就是工厂方法。2.2 单例模式维护性杀手用之前想清楚单例Singleton可能是被滥用最严重的模式。它的本意是保证一个类只有一个实例比如全局配置、连接池。但全局状态本身就是可维护性的敌人——你永远不知道谁改了它测试也没法隔离。我的建议是复习时掌握它的写法懒汉、饿汉、双重检查锁、静态内部类但实际项目里优先用依赖注入的方式管理单例生命周期而不是自己写getInstance()。如果用至少保证它是无状态的或者状态是只读的。考试里还常考线程安全问题。双重检查锁定Double-Checked Locking用volatile修饰实例变量这个细节一定要记住很多同学丢分就丢在没写volatile上。public class ConfigManager { private static volatile ConfigManager instance; private ConfigManager() {} public static ConfigManager getInstance() { if (instance null) { synchronized (ConfigManager.class) { if (instance null) { instance new ConfigManager(); } } } return instance; } }2.3 建造者模式参数多到爆时的解药当一个对象有七八个参数其中一半还是可选的时候构造函数重载会写到怀疑人生。建造者Builder模式把对象的构造过程拆成一步步的方法调用既清晰又能约束必填项。Pizza pizza new Pizza.Builder() .size(12) .cheese(true) .pepperoni(true) .bacon(false) .build();这个模式在复习时要注意和工厂模式区分工厂模式是“一步到位给你一个完整对象”建造者模式是“分步定制最后组装”。可维护性角度来说建造者模式最大的好处是新增可选属性时不用改构造函数只加一个方法调用方代码几乎不受影响。3. 结构型模式让类的形状可以“拼装”3.1 适配器新旧代码之间的翻译官适配器Adapter解决的是“接口不兼容”的问题。你有一个老系统的接口新代码期望另一种接口又不能大动干戈改老代码那就写一个适配器做转换。public class LegacyOrderService { public String queryOrder(String orderId) { ... } } public interface OrderQuery { OrderVO query(String id); } public class OrderQueryAdapter implements OrderQuery { private LegacyOrderService legacyService; public OrderVO query(String id) { String result legacyService.queryOrder(id); // 转换成OrderVO return convert(result); } }这个模式在“软件构造”课里是重点因为课程强调“增量式开发”——在不修改已有代码的前提下扩展功能。适配器完美体现了OCP老代码不动新代码面向新接口适配器在中间做桥接。3.2 装饰器不想动原类又想加功能装饰器Decorator和继承是两种“加功能”的思路。继承是静态的、编译期确定的而且一多层就产生类爆炸。装饰器是动态组合运行时一层层包裹每个装饰器只负责一项横切关注点。典型的例子就是Java IO流new BufferedInputStream(new FileInputStream(path))。BufferedInputStream就是一个装饰器它不改变文件读取的核心逻辑只加缓冲功能。public interface Notifier { void send(String message); } public class EmailNotifier implements Notifier { public void send(String message) { ... } } public class SmsNotifierDecorator implements Notifier { private Notifier wrapped; public void send(String message) { wrapped.send(message); sendSms(message); } }要加分可以再加一层比如WechatNotifierDecorator完全不用改EmailNotifier。这种“组合优于继承”的思想就是装饰器对可维护性的最大贡献。3.3 组合模式树形结构的统一操作组合Composite模式让“单个对象”和“组合对象”拥有一致的操作接口。典型场景是文件系统、组织架构、菜单树。public interface Component { void show(); } public class Leaf implements Component { ... } public class Composite implements Component { private ListComponent children new ArrayList(); public void add(Component c) { children.add(c); } public void show() { // 处理自身 for (Component c : children) c.show(); } }调用方根本不用关心自己处理的是叶子还是树节点统一调用show()就行。新增节点类型时只要实现Component接口就行不用改调用逻辑。这就是“部分-整体”结构的统一性带来的维护价值。3.4 外观模式给复杂子系统开一扇门外观Facade模式特别简单但特别实用。它给一个复杂的子系统提供一个统一的、简化的接口。调用方不需要知道子系统内部有几个类、互相怎么协作只知道一个Facade.placeOrder()就够了。维护性的提升在于子系统内部再怎么重构只要Facade的接口不变调用方就不受影响。这相当于给“变化”和“依赖”之间砌了一堵墙。考试里容易出现一个判断题外观模式是不是违反了最少知识原则恰恰相反外观模式正是最少知识原则的体现——调用方不需要和子系统里的所有类打交道只和一个外观类说话就可以了。4. 行为型模式把“变化的行为”变成“稳定的结构”4.1 策略模式算法的替换开关策略Strategy模式解决的是“算法族”的替换问题。不用策略模式的时候我们习惯用if-else或switch去选择算法每次加新算法都要改那个分支结构。用策略模式之后算法被封装成独立的类通过一个公共接口调用。public interface DiscountStrategy { double calculate(double amount); } public class VipDiscount implements DiscountStrategy { public double calculate(double amount) { return amount * 0.8; } } public class NormalDiscount implements DiscountStrategy { public double calculate(double amount) { return amount; } } public class OrderService { private DiscountStrategy strategy; public OrderService(DiscountStrategy strategy) { this.strategy strategy; } public double finalAmount(double amount) { return strategy.calculate(amount); } }以后加一个“节日促销折扣”新增一个类就完事OrderService一行都不用改。这是OCP最典型的落地。复习时要能把策略模式和状态模式区分开——策略模式是算法可以替换状态模式是行为随状态变化。4.2 观察者模式发布-订阅的解耦观察者Observer模式让“状态变化”的通知逻辑不再耦合在具体对象里。发布者只管通知不关心谁在听订阅者只管响应不关心通知从哪来。考试里常考Java的Observer/Observable接口还有GUI事件监听模型也是观察者模式的实例。可维护性的核心价值在于新增一种订阅者不需要修改发布者代码两边通过接口解耦。public interface OrderListener { void onOrderCreated(Order order); } public class OrderService { private ListOrderListener listeners new ArrayList(); public void addListener(OrderListener l) { listeners.add(l); } public void createOrder(Order order) { // 创建订单逻辑 for (OrderListener l : listeners) l.onOrderCreated(order); } }这里要注意一个细节观察者模式虽然解耦了发布者和订阅者但通知顺序、异常处理、线程模型都可能导致新问题。实际项目中这些监听器往往通过配置注册而不是在业务代码里硬编码addListener否则又变成了一种隐式耦合。4.3 模板方法骨架稳定细节可变模板方法Template Method定义了一个算法的骨架把一些步骤延迟到子类实现。它和策略模式的区别是策略模式是“整个算法替换”模板方法是“算法骨架固定部分步骤可变”。public abstract class DataImporter { public final void importData(String file) { validate(file); parse(file); save(); } protected abstract void parse(String file); protected void validate(String file) { // 默认实现 } protected abstract void save(); }这种模式能提升可维护性的关键在于“控制反转”——父类决定流程子类只提供细节。流程变了改父类细节变了改子类互不干扰。复习时要注意final关键字和protected访问级别的组合使用这是模板方法实现封装性的关键。4.4 状态模式状态机改起来不再头疼状态State模式把“对象在不同状态下有不同行为”这个逻辑从一堆if-else里解放出来。每个状态一个类状态之间的转移由状态类自己决定。这个模式对可维护性的提升非常直观没有状态模式时每加一个状态你要回到那个巨大的条件分支里翻找非常容易漏改。有状态模式后加一个状态就是加一个类改状态转移就改对应状态类的代码隔离性很好。考试常出的考点是状态模式 vs 策略模式。记一个核心区别策略模式的客户端自己决定用哪个策略状态模式中状态迁移是状态对象内部触发的客户端感知不到状态的变化。4.5 访问者模式双刃剑慎用访问者Visitor模式可以在不修改已有类的前提下为一系列类增加新的操作。听起来很美但它有个巨大的代价新增一个“被访问的类”时所有访问者都要改违反了OCP。它的适用场景很窄——对象结构稳定类很少新增但操作经常增加。比如编译器里对语法树的各种操作类型检查、代码生成、优化语法树的节点类型基本固定但操作不断增多这时访问者模式就很合适。复习到访问者模式时我的建议是理解意图和结构但答题时遇到不确定的场景别硬套。它是我见过实际项目里被用得最少、也最容易用错的模式。5. 复习时的常见误区与应试要点5.1 误区一看到需求变化就往模式上套这是所有新手都会犯的错。模式不是装饰品每个模式都有代价——类的数量变多、结构变复杂、阅读成本变高。一个只有两处变化的小场景用策略模式反而把简单问题复杂化了。我复习时给自己定了一个判断标准只有当变化是“频繁的、多维度的、且代码已经因为变化而变得难以维护”时才值得引入模式。如果在纠结要不要用那就先用最简单的方式写等第二次发生同样的变化时再重构。5.2 误区二只背意图不理解结构考试里常考“画出某模式的结构图”或者“根据结构图判断是哪种模式”。只背意图不记结构看到UML图就傻眼。建议至少把这些模式的参与者抽象类、具体类、客户端和它们之间的关系画上两三遍模式关键参与者核心关系策略Context, Strategy, ConcreteStrategyContext持有Strategy接口引用观察者Subject, Observer, ConcreteObserverSubject维护Observer列表装饰器Component, Decorator, ConcreteDecoratorDecorator包装Component工厂方法Creator, Product, ConcreteCreator子类决定创建哪个Product适配器Target, Adapter, AdapteeAdapter包装Adaptee实现Target状态Context, State, ConcreteStateContext持有State引用状态可切换5.3 答题的万能分析框架碰到“请为以下场景选择合适的设计模式”这类题我习惯按四步走找变化点题目里哪个因素是“可能变化的”业务规则、算法、对象创建方式、还是对象间交互方式找原则冲突现有设计哪里违反了OCP或者DIP哪个if-else分支将来会频繁改匹配模式变化点是“算法的选择”→策略变化点是“创建哪种具体类”→工厂变化点是“状态相关行为”→状态模式变化点是“通知依赖关系”→观察者。说清代价别只说优点顺带提一句“引入这个模式后类的数量增加但换取的是OCP的满足”分数会更高。注意答题时别把模式名写错或者混写。比如“模板方法”不是“模板模式”“外观”不是“门面模式”。这些术语在阅卷时都是采分点写错了很吃亏。6. 实操中踩过的坑与最后几点体会6.1 一个真实的重构例子去年我在一个模拟项目里接手过一段订单处理代码里面有一个200行的processOrder方法里面嵌套了四层if-else判断用户类型、判断商品类型、判断优惠类型、判断支付方式。每次加一种优惠那个方法就要再深一层测试同学已经放弃了写这个方法的用例。我做的第一件事不是套模式而是先提取变化点。把“优惠计算”单独拎出来定义成DiscountStrategy接口每种优惠一个实现类。然后是“通知逻辑”引入观察者订单创建后自动通知库存、财务、客服。最后是“支付方式”用工厂方法封装。改完之后processOrder瘦身到三十行新增优惠不再动主流程新增通知方也不用碰订单类。这个例子让我真正理解了——模式不是目标隔离变化才是目标。6.2 复习顺序建议如果你时间有限我建议按这个优先级复习第一梯队必考且高频策略、观察者、装饰器、工厂方法、模板方法第二梯队经常考适配器、组合、状态、单例、建造者第三梯队低频但会出选择题抽象工厂、外观、代理、访问者、迭代器、中介者6.3 最后一个小技巧复习模式的时候别只对着UML图看。试着问自己一个问题“如果我现在要给这个系统加一个新功能哪种模式能让我只新增一个类就完成”能在脑子里用这个标准过一遍你就不是在背模式而是在真正理解可维护性设计了。我当年就是把二十多个模式挨个用这个“新增功能”的思路过了一遍配合画结构图、默写关键代码考试时看到场景题基本不用思考太久直接就能锁定两三个候选模式。希望这份复习笔记也能帮你把“面向可维护性的设计模式”从背诵题变成真正的理解题。

相关新闻

在线询比价采购系统拆解:从传统比价到数字化采购

在线询比价采购系统拆解:从传统比价到数字化采购

不知道你有没有经历过这种场面:采购群里报价单满天飞,供应商发过来的Excel格式五花八门,有的报含税价,有的报不含税价,还有的在备注栏里补一句“运费另算”。等到开标的时候,几个人对着表格来回推算&#x…

2026/10/14 3:17:34 阅读更多 →
NLP文本表示与相似度计算:从TF-IDF到BERT的实战选型与调优

NLP文本表示与相似度计算:从TF-IDF到BERT的实战选型与调优

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

2026/10/14 3:17:34 阅读更多 →
毕业论文神器!2026年超实用AI论文网站榜单,免费版也能写合规初稿

毕业论文神器!2026年超实用AI论文网站榜单,免费版也能写合规初稿

2026 年实测 10 款主流 AI 论文工具,千笔AI以全流程覆盖 语义级降重 免费查重领跑综合榜;ThouPen 稳坐留学生毕业全流程工具头把交椅;免费工具中DeepSeek Scholar、豆包学术版表现亮眼,30 分钟即可生成万字高质量初稿&#xff0…

2026/10/14 3:17:34 阅读更多 →

最新新闻

从零构建每日晨报:信息过滤系统的设计与实现

从零构建每日晨报:信息过滤系统的设计与实现

1. 一份“每日晨报”到底在解决什么问题每天早上七点半,我习惯性打开自己维护的一个小工具,屏幕上会弹出一份排版干净的简报:昨天圈子里发生了什么、有哪些值得点进去看的链接、今天有什么值得留意的节点。这份东西我管它叫“每日晨报”&…

2026/10/14 3:59:41 阅读更多 →
TensorFlow工程实践:从数据流水线到模型部署的关键技术

TensorFlow工程实践:从数据流水线到模型部署的关键技术

TensorFlow这个标题一摆出来,懂行的人就知道,这不是在聊一个简单的开源库,而是在聊一套“深度学习工业化”的完整体系。我最早接触它的时候,还是在1.x时代,那时候写个模型要先搞placeholder、Session、global_variable…

2026/10/14 3:59:41 阅读更多 →
PyTorch本质:动态图张量系统与Python原生深度学习范式

PyTorch本质:动态图张量系统与Python原生深度学习范式

1. 项目概述:PyTorch不是“框架”,而是一套可编程的张量计算系统你点开这个标题,大概率是刚接触深度学习,被“PyTorch”三个字反复刷屏——课程推荐它、招聘要求它、论文代码用它、连隔壁做CV的同事都在说“我用PyTorch跑通了”。…

2026/10/14 3:59:41 阅读更多 →
小程序生日提醒应用开发全流程:云开发、定时触发与订阅消息实战

小程序生日提醒应用开发全流程:云开发、定时触发与订阅消息实战

简介:小程序作为轻量应用载体,常用于实现生日提醒、待办打卡等工具类场景。其核心价值不只是页面呈现,更在于数据模型设计与消息触达链路是否可靠。借助微信云开发环境,开发者可以免运维地使用云函数、文档型数据库与定时触发器&a…

2026/10/14 3:59:41 阅读更多 →
PyTorch深度学习实战:从动态计算图到模型部署的完整指南

PyTorch深度学习实战:从动态计算图到模型部署的完整指南

开篇先亮明我的态度:PyTorch这个框架,早就不只是“学术界玩具”了。如果你去翻各大厂的生产环境、顶会论文的复现代码、甚至Kaggle前排方案,十有八九跑的都是它。我最早从另一个框架转过来的时候,心里也犯嘀咕——动态图是不是太慢…

2026/10/14 3:59:41 阅读更多 →
SAS信号处理中停-走-停假设的局限与修正方法

SAS信号处理中停-走-停假设的局限与修正方法

1. SAS信号处理中的"停-走-停"假设概述合成孔径声呐(SAS)成像技术中,"停-走-停"假设是传统信号处理的基础模型。这个假设认为声呐平台在发射脉冲和接收回波时处于静止状态,实际运动只在脉冲间隔期间发生。听起来很理想化对吧&#x…

2026/10/14 3:58:41 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/12 18:11:17 阅读更多 →