00|Java 25 设计模式学习指南:24 种模式、OOP 思路与配套代码
设计模式适合从“代码为什么越来越难改”开始学习。增加一种导出格式,为什么要改很多控制器?给文本加两种能力,为什么子类成倍增加?文章进入已归档状态后,哪些操作应该被拒绝?这些问题分别涉及创建决策、对象组合和行为协作。模式给出的,是反复出现的问题及一组有代价的解决办法。
01|OOP 与工程基础:契约、组合、Java 25 与 Gradle
设计模式容易学成类图记忆题:看到接口就叫策略,看到包装就叫代理,看到静态方法就叫工厂。要避免这种情况,先建立一套检查对象设计的方法。本篇不新增一个模式,而是为后续24篇准备共同的语言和可运行工程。
02|简单工厂:把产品选择集中到一个入口
博客导出功能先支持纯文本,随后增加 Markdown。如果每个控制器都根据字符串判断应该 new 哪个格式化器,那么增加一种格式时,要逐个寻找这些分支。更麻烦的是,不同入口可能使用不同的拼写或默认值,同一配置在两个页面得到不同结果。
03|工厂方法:让子类决定流程使用的产品
导出任务已经有一条固定流程:校验标题,然后使用格式化器生成结果。不同导出器只在产品选择上不同。如果每个子类复制整段 export,后续修改标题规则时就可能漏改一个分支。
04|抽象工厂:一次切换相互匹配的产品族
博客后台支持浅色和深色主题,每个主题都有按钮和横幅。分别调用按钮工厂、横幅工厂时,很容易给深色页面装上浅色按钮。组件各自都能工作,但页面整体不一致。
05|建造者:把构建过程与有效对象分开
文章需要必填标题,也可以附带多个标签。继续扩充构造器会出现多个相似签名,调用处难以判断每个参数含义;如果先 new 空文章再调用 setter,又会让“标题尚未设置”的对象流入业务层。
07|单例:区分唯一实例、安全发布和共享状态
所有文章入口使用相同的标题长度上限。这个规则体积很小,初始化之后不变化,也不需要按请求重新创建。单例可以为这样的对象提供统一实例,但“到处都能拿到它”也会使依赖变得不明显。
08|适配器:把旧接口翻译成业务契约
文章发布服务希望调用 send(recipient, body),旧邮件组件却提供 deliver(content, address)。两个参数恰好都是 String,传反也能编译。若业务代码到处记住旧接口的顺序,未来替换组件时就要修改许多调用位置。
09|桥接:拆开业务类型与实现方式两条变化轴
通知有普通提示与警报两种业务类型,也有纯文本与 Markdown 两种呈现方式。如果用继承枚举组合,会出现 TextInfoNotice、MarkdownInfoNotice、TextAlertNotice、MarkdownAlertNotice;新增一种呈现方式后,还要再增加一整排组合类。