在学习设计模式之前,我强烈建议你先搞懂设计原则。

前言:为什么要先学设计原则?

用最通俗的话来解释:

  • 设计原则是“指导思想”——告诉你什么方向是对的

  • 设计模式是“通用模板”——告诉你针对某个具体问题怎么写代码是对的

如果你不了解原则,直接学模式,很容易出现几种情况:

  • 为了用模式而用模式,写出又重又绕的代码

  • 换个场景就不会用了,因为只记得“怎么写”,不明白“为什么这么写”

当你先明白了迪米特法则,再去看外观模式、中介者模式,你就会恍然大悟:

“原来这些模式就是为了实现迪米特法则而总结出来的一套经典写法,把复杂交互封装起来,只暴露简单接口。”

原则是道,模式是术。先悟道,再学术,事半功倍。


一、设计原则是什么?

词语定义:

设计原则是前辈们在长期的软件开发实践中总结出来的通用指导原则,用来帮助我们写出更容易维护、扩展、复用和理解的代码。

它不是具体的代码(不是库、不是框架),也不是外面的语法规则(不遵守代码也能跑),而是一种“最佳实践”或“编程好习惯”。

设计原则解决了什么问题?
无设计原则时常见现象:

  • 改一个功能,拆东墙补西墙
  • 加一个小需求,改十几种
  • 一种几千行,谁也不敢动

设计原则的目标就是避免上面这些情况

设计原则的本质

识别变化 + 隔离变化

好代码不是一成不变的,而是把容易变化的部分和稳定的部分分开,这样需求增加,你只需要改变“变化区”,不碰“稳定区”。

六大设计原则草案速览

原则 核心一句
开闭原则 增加功能多加新类,少改旧类
里氏替换原则 子类要能替换父类不出错
依赖倒置原则 依赖接口,不依赖具体类
接口隔离原则 接口别太胖,深入定制
单一职责原则 一类只干一件事
迪米特原则 别和陌生人说话

标准定义(Ian Holland, 1987):

Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.

中文翻译
一个软件实体应当对其它对象有尽可能少的了解——只跟最直接的朋友说话

通俗解释

  • 你可以调你自己方法的参数

  • 你可以调你自己创建的对象的方法

  • 你可以调你自己的成员变量(组件)的方法

  • 但你不要 通过“朋友的朋友”去调一个陌生人的方法

话语记住:

不要和陌生人说话,只和直接朋友交流。


二、迪米特法则的概念

本质 = 封装 + 委托 + 减少类间耦合

  • 封装:把对象内部结构(子对象、子对象的子对象)藏起来

  • 委托:当前对象把请求转发给自己的组件,不让外界越级访问

  • 减少耦合:一个类只依赖它“直接需要”的类,不依赖间接路径

迪米特法则并不是禁止使用链式调用,而是禁止跨越过多层次的 “非直接朋友” 调用。
像 LINQ 中的流畅接口(Fluent API)往往只在一层对象内返回同一类型的新对象,这种是安全的。


三、违反 vs 符合迪米特法则(C#代码对比)

违反迪米特法则:链式调用“朋友的朋友”

// 学校类
public class School
{
    public Department GetDepartment() => new Department();
}

// 部门类
public class Department
{
    public Teacher GetTeacher() => new Teacher();
}

// 教师类
public class Teacher
{
    public void Teach() => Console.WriteLine("老师在上课");
}

// 客户端:校长要老师上课
public class Principal
{
    public void OrderStartClass(School school)
    {
        // 违反迪米特法则:校长通过 school -> department -> teacher 链式调用
        school.GetDepartment().GetTeacher().Teach();
    }
}

问题分析

 

  • Proncipal既要知道 School,又要知道 Department,还要知道 Teacher

  • 一旦 Department 的获取方式改变(比如增加权限验证),所有类似的地方都要改

  • 耦合度过高,难以维护

符合迪米特法则:只和直接朋友通信

// 学校类:提供委托方法,封装内部链路
public class School
{
    private Department _dept = new Department();
    
    public void StartClass()
    {
        _dept.StartClass();  // 只告诉部门上课,不暴露内部细节
    }
}

// 部门类
public class Department
{
    private Teacher _teacher = new Teacher();
    
    public void StartClass()
    {
        _teacher.Teach();
    }
}

// 教师类不变
public class Teacher
{
    public void Teach() => Console.WriteLine("老师在上课");
}

// 客户端:校长只需要跟学校说话
public class Principal
{
    public void OrderStartClass(School school)
    {
        school.StartClass();  // ✅ 只和直接朋友 School 通信
    }
}

优势分析

  • 未来 Department 内部如何获取 Teacher 发生变化,只改 Department

  • Principal 与 TeacherDepartment 解耦

  • 每个类只了解自己的直接组件,符合“最少知识原则”

对比总结表

场景 违反迪米特法则 符合迪米特法则
调用方式 a.GetB().GetC().Do() a.Do() 内部协调 B 和 C
耦合程度 高,知道整条链路 低,只知道直接朋友
变化影响 链路中任何变化都要改客户端 只改直接朋友内部
代码可读性 需理解内部结构 语义清晰,一键调用

四、如何实现迪米特法则?

具体手段 说明
方法参数中的对象 可以直接调用它的方法(它是直接朋友)
当前方法内创建的对象 可以直接调用(它是直接朋友)
当前对象的成员变量 可以直接调用(它是直接朋友)
禁止 obj.GetSubObj().GetSubSubObj().Do() 把这条链封装给 obj 自己提供一个方法
使用中介者模式 当多个对象相互复杂引用时,引入中介者减少网状耦合

五、常见误区

误区 正解
“不允许任何链式调用” 链式调用如果是在同一对象上连续设置属性(如 builder.SetA().SetB().Build()),这是允许的,因为没有跨对象
“迪米特法则让中间类膨胀” 适当增加委托方法确实会增加一些代码量,但换来了模块独立性和低耦合。平衡点在于:如果委托方法仅仅是透传,且未来不太可能变化,可以酌情放宽
“这是过度设计,小项目不需要” 对于长期演进的系统(如业务核心、框架),迪米特法则非常有效。一次性脚本或简单原型可以适度忽略

六、结语:写得少 vs 写得多

你可能听过这样一句话:“能用最少的代码把需求实现出来的程序员,才是高手。”

这确实是。但学了迪米特法则之后,你可能会产生一个疑问:

迪米特法则让我写委托方法、做封装、避免链式调用,代码量反而变多了。这不是矛盾吗?

答案是:不矛盾,只是观察“代码量”的时间维度不同。

 

短视角(只看今天)

 

  违反迪米特法则 符合迪米特法则
今天写代码量 少(直接链式调用,一把梭) 多(写委托方法、做封装)
今天的感受 ✅ 高效 ❌ 啰嗦,过度设计

纵向视角(看三个月、一年后)

  违反迪米特法则 符合迪米特法则
第5次需求变更 一定要改掉老代码,类越来越多的营销事件 加新类就行,老代码纹丝不动
累计代码变化量 改大量老代码 只加了新代码
引入Bug的风险 高(改一处可能炸多处) 低(新代码不影响老逻辑)
维护成本 指数级上升 线性生长

一句话破

“用最少的代码实现需求”说的是:不要写无用、重复的代码。

设计原则要求你“多写”的是:为未来的变化装备空间的代码,是隔离变化的第三个,是让整个系统能够活过第二次、次需求变更的代码。

 

两者不冲突:

  • 无原则的少写 → 今天爽,三个月后想重构

  • 有原则的多写 →今天多花10分钟,半年后少花10个小时

真正的厉害

厉害的程序员不是在今天用最少的代码把需求糊出来。

而是知道哪些代码可以少写,哪些代码必须多写,并且能准确判断这个“度”。

 

迪米特法则给了你一个判断标准:

  • 直接朋友 → 可以少写(本来就是清晰的依赖)

  • 陌生人的链式调用 → 必须多写一层委托(隔离变化)


金句收尾

代码写得少,不一定是高手;
但能让半年后的同事(包括你自己)少掉头发,一定是高手。

迪米特法则,就是那把“管住手”的护发神器。


下一篇预告

单一职责原则 —— 你的类怎么这么多?

设计模式的开门砖 - 六大设计原则 - 单一职责原则 - C# https://blog.csdn.net/ldjt0729/article/details/162259150?spm=1001.2014.3001.5501


如果觉得文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我更新的动力!

 

 

更多推荐