新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java 设计模式之 Abstract Document(抽象文档模式):在强类型语言中实现动态属性与类型安全的平衡

发布时间:2026/10/2 1:56:33来源:尧图网络
Java 设计模式之 Abstract Document(抽象文档模式):在强类型语言中实现动态属性与类型安全的平衡
示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载导读Abstract Document 是 java-design-patterns 项目中收录的结构型Structural设计模式之一其核心思想是让对象以“键值对key-value存储”的方式持有属性同时通过一组名为trait特质的接口对外暴露**类型安全type-safe**的静态访问视图。本文以 localization/vi/abstract-document/README.md 为主线结合仓库中 abstract-document 模块的完整源码与测试讲解该模式的目标、类结构、逐行实现、运行效果与适用场景。读完本文你将能独立实现一个既能在运行时动态增删属性、又能保持编译期类型安全的 Java 数据模型。一、模式目标兼得“动态”与“类型安全”Java 是一门强类型语言属性通常在编译期就已固定。而 Abstract Document 模式的**目标Mục tiêu**正是在享受无类型弱类型、动态语言灵活性的同时保留强类型语言的类型安全特性。一句话概括A simple way这个模式的做法允许向对象附加属性而对象本身完全不必知道这些属性的存在。维基百科对该模式的经典描述原文文档中引用进一步明确了它的定位这是一种面向对象的结构型设计模式用于将对象组织成弱类型的键值存储并通过**类型化视图typed views**暴露数据。其目的是在强类型语言中实现组件之间的高度灵活性——可以在运行时on the fly向对象树添加新属性同时不丢失类型安全的支持。模式利用 trait 将类的不同属性分离到不同接口中。从仓库源码的类注释可以看到同样的表述见 AbstractDocument.javaThe Abstract Document pattern enables handling additional, non-static properties. This pattern uses concept of traits to enable type safety and separate properties of different classes into set of interfaces.即核心矛盾是“运行时灵活性”与“编译期安全”的冲突而模式给出的解药是——底层用 Map 存储一切上层用接口trait定义访问方法。二、现实场景为什么需要动态属性原文文档用汽车举例一辆汽车由许多零部件组成。但我们事先并不知道某一辆具体的车到底包含哪些部件——是全部还是只有其中一部分。可以说这些汽车是动态的、极其灵活的。这个例子精确地刻画了该模式的适用场景实体集合具有共性但每个实体的属性集在运行时才确定。图书馆系统中纸质书、电子书、有声书各具不同的字段页数、文件大小、时长是同一个道理——用一组公共属性 各自的扩展属性来建模而不是为每种书各写一个死板的类。三、类结构三层架构拆解原文文档给出了类图abstract-document.pngAbstract Document Traits and Domain。Abstract Document 模式的 Traits 与领域类图结合源码 abstract-document/src整个模式的类结构可分为三层1. 核心抽象层Document接口与AbstractDocument抽象类包路径com.iluwatar.abstractdocument位于 Document.java 与 AbstractDocument.java。public interface Document { Void put(String key, Object value); Object get(String key); T StreamT children(String key, FunctionMapString, Object, T constructor); }接口只声明三个方法put写入属性、get读取属性、children递归取出子文档流。AbstractDocument负责实现它们public abstract class AbstractDocument implements Document { private final MapString, Object documentProperties; protected AbstractDocument(MapString, Object properties) { Objects.requireNonNull(properties, properties map is required); this.documentProperties properties; } Override public Void put(String key, Object value) { documentProperties.put(key, value); return null; } Override public Object get(String key) { return documentProperties.get(key); } Override public T StreamT children(String key, FunctionMapString, Object, T childConstructor) { return Stream.ofNullable(get(key)) .filter(Objects::nonNull) .map(el - (ListMapString, Object) el) .findAny() .stream() .flatMap(Collection::stream) .map(childConstructor); } // toString() 亦已实现…… }逐点说明这层设计的精妙之处存储即 Map所有属性以MapString, Object保存天然支持任意类型、任意键这就是“无类型 / 动态”的一面。构造约束构造函数用Objects.requireNonNull(properties, properties map is required)强制属性表非空测试 AbstractDocumentTest.java 中的shouldHandleExceptionDuringConstruction正是验证传入null会抛出NullPointerException。children的管道式实现先把get(key)包进Stream.ofNullable过滤空值再强转为ListMapString, Object取第一个元素展平为流最后用传入的childConstructor函数把每份属性表构造为子对象。注意findAny().stream()保证了当键不存在或值不是列表时返回空流而非报错——对应测试 shouldRetrieveEmptyStreamForNonExistingChildren。toString附加实现AbstractDocument还重写了toString()将属性表格式化为类名[键 : 值, ...]形式见 AbstractDocument.java测试shouldIncludePropsInToString对其做了断言。2. 属性枚举层Property枚举Property.java 集中定义属性键避免魔法字符串散落各处public enum Property { PARTS, TYPE, PRICE, MODEL }在后续代码中统一通过Property.TYPE.toString()等方式取键名枚举常量即成为 Map 键的“字典”。3. trait 视图层四个接口包路径com.iluwatar.abstractdocument.domain每个接口都继承Document并用default 方法把 Map 中的原始值包装成类型化视图public interface HasType extends Document { default OptionalString getType() { return Optional.ofNullable((String) get(Property.TYPE.toString())); } } public interface HasPrice extends Document { default OptionalNumber getPrice() { return Optional.ofNullable((Number) get(Property.PRICE.toString())); } } public interface HasModel extends Document { default OptionalString getModel() { return Optional.ofNullable((String) get(Property.MODEL.toString())); } } public interface HasParts extends Document { default StreamPart getParts() { return children(Property.PARTS.toString(), Part::new); } }这里的“trait”正是维基百科定义中提到的核心机制每个接口负责一种属性的类型化访问。OptionalT的返回值让缺失属性变得显式、安全调用方可以链式使用orElseThrow()/orElse(null)处理缺省情况getParts()则复用children管道将子属性表逐个构造成Part对象。4. 领域实体层Car与Part两个实体都只是“继承 组合 trait”的极薄类public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { public Car(MapString, Object properties) { super(properties); } } public class Part extends AbstractDocument implements HasType, HasModel, HasPrice { public Part(MapString, Object properties) { super(properties); } }对应源码Car.java、Part.java。Car组合HasModel/HasPrice/HasParts三个 traitPart组合HasType/HasModel/HasPrice。注意领域类本身没有声明任何字段、没有实现任何访问方法——所有“看起来是静态方法”的getModel()、getParts()都来自 trait 的 default 方法这就是“对象不知道属性存在”的具象体现。从 UML 源文件 abstract-document.urm.puml 中可以看到AbstractDocument实现DocumentCar、Part继承AbstractDocument并分别实现对应 trait 接口四个 trait 接口又全部继承Document形成完整的菱形继承体系。四、完整示例构造一辆“动态汽车”原文文档给出了从零件到整车的完整构造过程仓库中的入口类 App.java 与之一一对应public static void main(String[] args) { LOGGER.info(Constructing parts and car); var wheelProperties Map.of( Property.TYPE.toString(), wheel, Property.MODEL.toString(), 15C, Property.PRICE.toString(), 100L); var doorProperties Map.of( Property.TYPE.toString(), door, Property.MODEL.toString(), Lambo, Property.PRICE.toString(), 300L); var carProperties Map.of( Property.MODEL.toString(), 300SL, Property.PRICE.toString(), 10000L, Property.PARTS.toString(), List.of(wheelProperties, doorProperties)); var car new Car(carProperties); LOGGER.info(Here is our car:); LOGGER.info(- model: {}, car.getModel().orElseThrow()); LOGGER.info(- price: {}, car.getPrice().orElseThrow()); LOGGER.info(- parts: ); car.getParts().forEach(p - LOGGER.info(\t{}/{}/{}, p.getType().orElse(null), p.getModel().orElse(null), p.getPrice().orElse(null))); }值得注意的实现细节嵌套结构即对象树carProperties的PARTS键值是一个List.of(wheelProperties, doorProperties)这正是“树形层级数据”的天然表达Car是第一层节点Part是第二层子节点且两层共用完全相同的 Map 驱动方式可以继续无限嵌套。Map.of 的限制示例使用Map.of构造属性表它最多支持 10 对键值且不可为 null当属性更多时改用HashMap或Map.ofEntries即可。运行时增删属性由于底层就是 Map调用car.put(color, red)即可在运行时动态追加属性而Car类本身无需任何改动——这正是“on the fly 添加属性”的直接验证。运行后的输出原文文档与 App 源码一致Constructing parts and car Here is our car: - model: 300SL - price: 10000 - parts: wheel/15C/100 door/Lambo/300类型化的getModel().orElseThrow()返回StringgetPrice().orElseThrow()返回NumbergetParts()返回StreamPart——调用方拿到的是强类型视图而非原始 Object类型安全由此保证。五、何时使用 Abstract Document 模式原文文档的“应用Ứng dụng”一节给出了三条判据满足其一即可考虑该模式有在运行时on the fly添加新属性的需求希望以树形结构灵活组织业务数据希望系统耦合更松散loose coupling。结合英文版 READMEabstract-document/README.md的扩展论述以下真实场景与之一一呼应内容管理系统CMS文章、图片、视频共享“创建时间/作者/标签”又各有“图片分辨率/视频时长”等专属字段文件系统文档、图片、音频、目录共享“文件大小/创建时间”又各有“分辨率/时长”等扩展属性电商平台实物商品、数字下载、订阅共享“名称/价格/描述”又各有“运费/下载链接”等差异字段医疗档案人口学信息、病史、检验结果、处方共享“患者 ID/出生日期”各自又有专属数据配置管理不同类型配置元素各有属性集但需要统一一致的访问与操作方式教育平台 / 项目管理工具不同形态的学习材料、不同类型的任务待办、里程碑、缺陷都存在“共性 个性”的属性结构。共同特征可以概括为属性结构多样化且在演进diverse and evolving attribute structures、动态添加属性是常态、数据访问需要与具体格式解耦、可维护性与灵活性至关重要。六、收益与代价原文文档在“收益与取舍”维度给出的结论也完全符合本仓库实现收益Benefits灵活性Flexibility容纳各种不同的文档结构与属性可扩展性Extensibility动态新增属性而不破坏既有代码——只需向 trait 体系追加一个新接口或直接向 Map 写入新键可维护性Maintainabilitytrait 将“属性访问”按关注点分离代码整洁且易适配可复用性Reusability类型化视图使同一套访问逻辑可以在多个实体间复用如HasModel同时服务于Car与Part。代价Trade-offs复杂度Complexity需要为每个属性定义接口与视图带来额外的实现开销性能Performance相比直接字段访问多了一层 Map 查找、类型转换与 Stream 管道开销对于高频热路径需要权衡从children的实现可推断每次调用都会经过ofNullable → filter → map → findAny → flatMap → map整条管道。七、如何运行与验证本仓库为 Maven 多模块工程abstract-document是独立子模块可直接运行入口类验证模式行为# 在仓库根目录运行该模块的入口类App.main ./mvnw -pl abstract-document compile exec:java \ -Dexec.mainClasscom.iluwatar.abstractdocument.App # 或运行该模块的全部单元测试 ./mvnw -pl abstract-document test测试目录 abstract-document/src/test/java/com/iluwatar/abstractdocument 中的三个测试类覆盖了模式的关键行为AbstractDocumentTestput/get读写、子文档流数量、缺失键返回空流、toString包含键值、空属性表抛NPE、嵌套文档存取、值覆盖更新DomainTest针对Car/Part领域实体的类型化视图访问AppTest入口类冒烟测试。八、参考资料原文文档给出的参考文献外部链接请自行查阅原文档或相关书籍Wikipedia: Abstract Document PatternMartin Fowler: Dealing with propertiesPattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)仓库内可继续深入研读的还包括英文版 abstract-document/README.md含更详尽的适用场景与收益取舍分析、类图源文件 abstract-document.urm.puml 以及完整源码目录 abstract-document/src/main/java/com/iluwatar/abstractdocument。总结Abstract Document 模式用一句话可以概括为底层一张 Map上层一堆 trait 接口。它把“动态属性存储”交给MapString, Object把“类型安全访问”交给继承Document的 default 方法接口再由具体实体Car、Part通过多重实现自由组合所需视图。当你的业务模型属性高度动态、结构呈树形且需要松散耦合时这个模式提供了一条不牺牲类型安全的灵活路径——正如仓库中的汽车示例所示一辆车到底有没有某个零件、有哪些零件完全可以等到运行时再决定。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐Java 设计模式之 Abstract Document 抽象文档模式在类型安全与动态属性之间取得平衡Java 设计模式之 Abstract Document 抽象文档模式在类型安全与动态属性之间取得平衡 Abstract Document抽象文档模式是示例工程教程Java 设计模式实战Abstract Document抽象文档模式——在类型安全下实现动态属性与灵活数据树Java 设计模式实战Abstract Document抽象文档模式——在类型安全下实现动态属性与灵活数据树 Abstract Document抽象文档示例工程教程Java 设计模式实战Abstract Document 抽象文档模式——用类型安全的方式动态管理树状属性结构Java 设计模式实战Abstract Document 抽象文档模式——用类型安全的方式动态管理树状属性结构 本篇技术指南以 java design pat示例工程教程上一篇Gifski内存占用优化处理4K视频时避免Mac卡顿的实用技巧下一篇ComfyUI 视频生成插件入门从一条文字到一段视频的完整路径创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

停车场系统低层设计实战:基于 awesome-low-level-design 的 Java 多楼层停车管理系统 2026/10/2 7:55:27

停车场系统低层设计实战:基于 awesome-low-level-design 的 Java 多楼层停车管理系统

示例工程 【免费下载链接】awesome-low-level-design Learn Low Level Design (LLD) and prepare for interviews using free resources. 项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-low-level-design 点击查看 免费下载 导读 本文以 awesome-low…

阅读更多 →
OpenCV工业缺陷检测实战:solvePnP姿态估计与intersectConvexConvex几何判断 2026/10/2 7:55:27

OpenCV工业缺陷检测实战:solvePnP姿态估计与intersectConvexConvex几何判断

1. 从一个“抓缺陷”的需求说起1.1 这个实例到底在做什么“抓出三个缺陷”这个标题听起来像是工厂质检线上的活儿,实际上它确实是。这个 OpenCV 实例要解决的问题很具体:在一张工业零件或者产品的图像里,自动找出三个预先定义好的缺陷区域&am…

阅读更多 →
uuv_simulator水下机器人仿真环境搭建:从ROS安装到Gazebo部署全流程 2026/10/2 7:55:26

uuv_simulator水下机器人仿真环境搭建:从ROS安装到Gazebo部署全流程

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

阅读更多 →
AISystem 模型轻量化之 FBNet 系列:从 DNAS 搜索到 JointNAS 联合优化 2026/10/2 7:55:26

AISystem 模型轻量化之 FBNet 系列:从 DNAS 搜索到 JointNAS 联合优化

文档教程人工智能 【免费下载链接】AISystem AISystem 主要是指AI系统,包括AI芯片、AI编译器、AI推理和训练框架等AI全栈底层技术 项目地址: https://gitcode.com/GitHub_Trending/ai/AISystem 点击查看 免费下载 FBNet 是 Facebook 提出的轻量级卷积神…

阅读更多 →
AI辅助做PPT的正确姿势:从大纲到排版,30分钟产出可交付演示文稿 2026/10/2 7:55:26

AI辅助做PPT的正确姿势:从大纲到排版,30分钟产出可交付演示文稿

1. 先搞懂AI做PPT的正确姿势:它不是替身,是杠杆网上聊AI做PPT,十个人里有八个人说的是"一键生成完整PPT"。输个主题,等十几秒,出来一套二三十页的成品,看着挺唬人,真拿去开会或者交作…

阅读更多 →
金融PRD从规则到PDF:状态机、幂等与对账的完整写法指南 2026/10/2 7:55:19

金融PRD从规则到PDF:状态机、幂等与对账的完整写法指南

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉