之前的一篇文章提到过关于对象的命名,核心观点是:命名应反映业务角色与专业性,以提升代码的可读性、内聚性与可扩展性,最终提升可维护性。
而可维护性是软件的系统的核心。
但理解这个概念可能需要一些背景知识和时间,那么如何快速上手?本篇文章对这些概念进行简单的阐述,并通过一个案例说明命名的进步过程。
核心领域与非核心领域的命名差异
对象命名首先需要区分我们在命名什么,对象所处的位置是核心领域(Core Domain)还是非核心领域(Non-Core Domain)。
-
-
- 核心领域是指承载业务本质的关键部分,直接反映系统的核心价值。例如,在学校管理系统中,"课程表(CourseSchedule)"是核心领域,负责协调课程、教师和教室资源;在电商系统中,"订单(Order)"则是核心领域,涵盖商品购买的生命周期。核心领域的命名应高度贴近业务术语,体现专业性与角色职责。
- 非核心领域则是支持性的技术性模块,如日志记录(Logging)、数据访问(DataAccess)。这些部分的命名可以适当使用技术性后缀(如"Manager""Repository"),以突出其辅助角色。
-
为何区分? 因为核心领域的命名直接影响业务逻辑的清晰度和系统的可维护性,而非核心领域更注重技术实现的通用性。例如,"CourseSchedule"比"ScheduleManager"更能直观表达课程安排的业务角色,而"LogManager"则适合非核心的日志功能。如果对应到DDD,这两部分通常被划分为核心域与支持域。
为什么核心域需要更贴近业务?核心域是业务的核心竞争力所在,需要高度的定制化和精确性。因此,应该避免使用过于通用的命名,以免掩盖业务的独特性。
命名尽量贴近领域,贴近现实世界
开头我们首先区分了核心领域与非核心领域,下面我们说的规则主要应用与核心领域。
对于核心领域,命名需要更多从现实世界中获取,这是因为现实世界的概念已经过长期发展和演化: 现实世界中的角色、领域概念,例如"顾客"、"订单"、"银行账户"、"医生"、"病人"等等, 并非凭空捏造,而是经过漫长的社会实践、商业活动、科学研究等发展而来。它们在实践中不断被验证、修正和完善,其边界、责任和功能通常已经非常清晰和成熟。
同时,现实世界中的对象由于较为常见,日常生活中已经理解这些对象的概念与意义,不再需要在软件中增加额外的负载(还记得上学时做英语阅读理解,如果主题和日常相关,很多单词不认识也能大概猜出文章意思,如果文章太抽象,提到的内容不在日常生活内,即使每个单词都认识,还是难以理解文章概念?)
下面是一个简单Demo说明,一个学校排课系统,通过合理的命名,能够延伸出显性和隐性的职责。
/**
* 代表学校教学安排的领域对象
* 核心职责是管理课程安排与资源协调
*/
public class CourseSchedule {
// 核心职责(显性)
private Map<TimeSlot, Classroom> assignedRooms;
public void assignTeacher(Course course, Teacher teacher) {
/* 分配授课教师并验证资质 */
}
// 核心职责(显性)
public boolean checkTimeConflict(LocalDateTime newSlot) {
/* 检查新时段是否与现有课程冲突 */
}
// 不应有的职责:
// 1. 不处理学生考勤(属于AttendanceSystem)
// 2. 不计算教师薪资(属于PayrollSystem)
// 3. 不管理教学设备(属于TeachingEquipmentResource)
// 隐含职责(后续扩展)
public void generateIcalFeed() {
/*
* 新增需求:生成日历订阅链接
* 虽未在类名中体现,但现实中课程表天然需要时间同步功能
* 添加此方法符合对象职责的自然延伸
*/
}
}
不熟悉领域
上面的CourseSchedule表类虽然日常生活中容易理解,但也有一定的领域知识,如果我们一开始对领域并不熟悉,该怎么办?例如,"CourseSchedule"涉及学校管理的专业知识,作为开发者一开始比较懵逼,该怎么办?
从现实角色入手
通常来说,开发某个领域的代码,都可以先简单了解该领域的基础知识与词汇,当然有条件最好能与这个行业有经验的人沟通,也就是所谓的领域专家,比如CourseSchedule这个例子,可以提取名词--关键实体(如"课程""教室")和动词(如"安排""冲突检查"),逐步构建命名。
避免缺乏灵魂的技术后缀
如果一开始想不好名字,那么可以先尽可能避免使用"er""or"等模糊后缀,如"Scheduler""Processor",这些命名缺乏领域语义,难以反映业务角色,这可能导致丢失业务的精确度与定制性。但是本身已成为领域实体的词汇除外,例如"Teacher"。
渐进式演化
最开始如果并没有想到比较好的名字,可以从简单开始随着开发的进行,学习到新的领域,逐步演进,依然是CourseSchedule这个例子:
第1阶段 - ClassData(纯数据容器)
在这一阶段,命名仅关注数据的存储,缺乏业务语义的表达。ClassData作为一个纯数据容器,虽然简单易懂,但其局限性在于没有体现课程安排的业务角色,仅停留在技术层面的数据收集,职责边界模糊,无法反映现实世界的逻辑。
/**
* 简单的课程数据类
*/
public class ClassData {
private List<String> classes;
private List<String> teachers;
private List<String> rooms;
public void addClass(String className, String teacher, String room) {
/* 简单添加数据 */
}
}
第2阶段 - ClassSchedule(引入业务概念)
相比ClassData,ClassSchedule迈出了关键一步,开始引入业务概念,命名从单纯的数据容器转向课程安排的初步表达。进步在于通过teacherAssignments和roomAssignments映射了教师和教室的分配关系,增加了简单的业务逻辑(如时间可用性检查)。然而,其局限性依然明显:命名仍偏技术化,未能完全贴近现实世界的"课程表"概念,职责定义较为粗浅,缺乏对领域规则的深入体现。
/**
* 课程安排类
*/
public class ClassSchedule {
private Map<String, String> teacherAssignments; // 教师分配
private Map<String, String> roomAssignments; // 教室分配
public void assignClass(String time, String teacher, String room) {
/* 基础的课程安排逻辑 */
}
public boolean isTimeAvailable(String time) {
/* 简单的时间检查 */
}
}
第3阶段 -CourseScheduleManager(领域职责初具雏形)
相比ClassSchedule,CourseScheduleManager在业务表达上更进一步,通过引入Course和Teacher等领域实体,命名开始贴近学校管理的现实逻辑。进步体现在职责的细化,如教师分配加入了业务规则,时间冲突检查也更具体,体现了领域知识的深化。然而,其局限性在于"Manager"后缀仍带有技术化色彩,未能完全摆脱抽象管理的意味,可能模糊了"课程表"作为核心业务角色的直观性,限制了命名的语义精度。
/**
* 课程表管理类
*/
public class CourseScheduleManager {
private Map<TimeSlot, Classroom> roomAssignments;
private Map<Course, Teacher> teacherAssignments;
public void assignTeacher(Course course, Teacher teacher) {
/* 增加了教师分配的业务规则 */
}
public boolean checkScheduleConflict(TimeSlot slot) {
/* 增加了时间冲突检查 */
}
}
最终阶段 - CourseSchedule(完整领域对象)
相比CourseScheduleManager,CourseSchedule完成了从技术视角到领域视角的转型,彻底贴近现实世界的"课程表"概念。进步在于去除了"Manager"后缀,直接以业务角色命名,职责边界更清晰,方法如generateIcalFeed自然延伸了课程表的时间属性,展现出可扩展性。此时已无明显局限性,命名与业务本质高度契合,直观&专业。
/**
* 代表学校教学安排的领域对象
* 核心职责是管理课程安排与资源协调
*/
public class CourseSchedule {
private Map<TimeSlot, Classroom> assignedRooms;
public void assignTeacher(Course course, Teacher teacher) {
/* 分配授课教师并验证资质 */
}
public boolean checkTimeConflict(LocalDateTime newSlot) {
/* 检查新时段是否与现有课程冲突 */
}
public void generateIcalFeed() {
/* 生成日历订阅链接 */
}
}
小结
命名是软件设计中看似微小却至关重要的环节,命名不仅仅是简单的代码问题,还是我们对业务本质理解水平的呈现,更是业务逻辑与领域专家沟通的关键。通过区分核心领域与非核心领域,我们明确了命名应优先服务于业务本质,尤其在核心领域中,贴近现实世界的概念能带来语义清晰、职责明确和可维护性提升。