迪米特法则讲解
迪米特法则,还有叫最少知道原则的,我们现在再来看一下定义,一个对象应该对其他对象保持最少知道的了解,
又叫最少知道原则,尽量降低类与类之间的耦合,那迪米特主要强调的是降低耦合,优点也自然是降低类之间的耦合,
那从代码层面呢,最少知道,也就是该知道的知道,不该知道的不知道,尽量不要对外公开太多的public方法,
和非静态的public变量,尽量内敛,多使用private,包权限,还有protected等访问权限,那迪米特原则的核心观念,
是类之间的解耦,那解耦是有一定程度的,我们尽量做到弱耦合,只有耦合越低,类的复用率还可能提高,减少了每个
类之间不必要的依赖,从而降低耦合的一个关系,凡是要有个度,之前我也有强调,你如果过分的使用迪米特原则,
会产生大量的中介类,导致系统变复杂,为维护带来了难度,所以我们在使用迪米特的原则,要反复权衡,要做到
结构清晰,需要做到低耦合,高内聚,那平时我们在做编码的时候,经常碰到一种场景,也就是说一个方法,我放到A类也行,
放到B类也行,那这种情况我们该怎么做呢,我们可以坚持一个原则,如果一个方法放在一个本类中,既不增加类之间的关系,
也对本类不产生影响,那就可以放在本类中,那一会我们在coding的时候,引着大家来看UML类图
迪米特原则我们主要强调什么,强调只和朋友交流,不和陌生人说话,在这里面我们就要讲一个概念,在迪米特原则中,
朋友出现在成员变量,方法的入参和出参中,这里面的类称之为朋友类,而出现在方法体内部的类,是不属于朋友类的,
不和陌生人说话,就是不该了解的类我们不需要
那迪米特法则也叫最少知道原则,那最少知道原则就比较好理解了,我不应该知道的我就不应该知道,那这句话就是一句
废话,简单的理解,他的表现形式呢,是我对外部引入的类,越少越好,但是目的和结果,是一致的,要完成这个类和方法的一个
原则,强调的是只和直接的朋友交流,那么每个类和其他对象,肯定是有耦合关系的,那朋友关系组合聚合,依赖等等这些关系,
都可以称之为朋友关系,迪米特是为了降低类之间的耦合,尽量减少对其他类的依赖,这样才能使系统得到独立,相互之间不
存在,减少依赖关系,提高内聚,那我们新建一个包,那我现在有一个业务场景,比如说,大老板说你给我查一下,到现在为止
线上有多少个课程,那这里面关系到几个类
Test应用的client,主要是看这三个,这个排版是IDEA自动排版的,根据不同的元素它会自动排版,
那这个排版就比较清晰,Course不应该是由Boss来创建的,而应该是由TeamLeader,指向Course的箭头
应该是由TeamLeader来指向他,这个就是从UML角度来分析的,迪米特法则,Test是应用层,把它认为是客户端,
他来调整体的服务,他来创建Boss和TeamLeader,这个先忽略,主要是看他们三的一个关系,那我们现在来修改一下实现
package com.learn.design.principle.demeter;
/**
* 首先Boss这里面有一个方法
*
* 有很多人觉得这么写也没有什么问题
* 我们一起来分析一下
* 看他到底存不存在一些可以改进的点
* 那我们看一下Boss的这个方法
* 之前我们也强调过
* 迪米特法则主要讲的是只和直接的朋友交流
* 例如Boss
* TeamLeader他作为一个入参
* 它是直接的朋友
* 朋友的定义
* 就是出现在类成员变量里
* 比如这里有一个成员变量
* 还有方法的入参
* TeamLeader
* 还有返回值输出
* 也就是说方法的输入和输出
* 也都是直接的朋友
* 不过我们现在这里是一个void
* 而方法体内部的类
* 不算朋友
* 例如这个Course这个类
* 他并不算是Boss的朋友
* 所以Course不是Boss的朋友
* Boss直接给TeamLeader下指令
* TeamLeader查完之后
* 直接把结果给Boss就可以了
* Boss不需要关注
* 他和TeamLeader是直接的关系
* 不应该和陌生的Course类发生交流
* 那目前的这种写法呢
* 就违背了迪米特法则
* 如果Boss TeamLeader Course并不在同一个包下
* 我们就会发现在这个类里面
* import Course这个类
* import Course这个类并没有出现在直接朋友的这个位置
* 例如成员变量方法的入参
* 还有返回值
* 但是我们也要import导入进来
* 那这个其实在某种情况下一种判断的方式
* 那我们看一下类图
* 我们看完类图
* 就会比较清晰
* 同时我们看一下他们之间的变化
* 包括我们改进之后的
*
*
*
* @author Leon.Sun
*
*/
public class Boss {
/**
* Boss查课程的一个数量
* 方法名就叫这个了
* 这里面传入一个TeamLeader
* 给TeamLeader下指令
* 让他查课程的数量
* 那现在Boss向TeamLeader下指令
* 首先初始化一下课程
*
* 这里面已经不需要再知道和了解Course类了
* 而把这个移到TeamLeader里面
*
* 下指令
* 给TeamLeader下指令
* TeamLeader直接查
* Boss不需要了解Course这些类
* 然后TeamLeader去和Course类发生接触
* 他来查具体的数量
*
*
*
* @param teamLeader
*/
public void commandCheckNumber(TeamLeader teamLeader){
/**
* 这里调用checkNumberOfCourses方法
* 把集合传进来
*
* 然后Boss这里边参数也不用传了
*
*/
teamLeader.checkNumberOfCourses();
}
}
package com.learn.design.principle.demeter;
/**
*
* @author Leon.Sun
*
*/
public class Course {
}
package com.learn.design.principle.demeter;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Leon.Sun
*
*/
public class TeamLeader {
/**
* 这里直接把集合课程传进来
*
* 这个时候入参就可以干掉了
*
*/
public void checkNumberOfCourses(){
/**
* 赋值new一个ArrayList
* 导一下包
*/
List<Course> courseList = new ArrayList<Course>();
/**
* 那现在开始查数
* 这里面把它初始化
* 一个一个查
* 假设这里面用一个比较笨的方法
* 下一页下一页的往下查
* 写一个SQL直接从数据库中查
* 所以我们模拟一下一页一页的查数
* 那小时候我们也听过
* 数鸭子的儿歌
* 我们采用和数鸭子一样的方式
* 来数课程
* 假设一共20个
*
*
*/
for(int i = 0 ;i < 20;i++){
/**
* 直接往里面add一个Course
* Course对象放在这里
*
*/
courseList.add(new Course());
}
/**
* 直接输出
*
*/
System.out.println("在线课程的数量是:"+courseList.size());
}
}
package com.learn.design.principle.demeter;
/**
* 这里面的逻辑也非常的简单
*
* Course类不变
* 我们回到Test里边
* 这个写法也没有变化
* 结果是一样的
* 我们遵循这个原则之后呢
* 在某些业务场景下
* 应用层代码是不需要变化的
* 当然需要复杂的场景也是需要变化的
* 目前我们从Test里面我们看不出变化
* 那我们来看一下UML
* 这里面就很清晰了
*
* @author Leon.Sun
*
*/
public class Test {
public static void main(String[] args) {
/**
* 首先我们new一个Boss
*
*/
Boss boss = new Boss();
/**
* 然后new一个TeamLeader
*
*/
TeamLeader teamLeader = new TeamLeader();
/**
* 这个时候我们 commandCheckNumber
* 把teamLeader传进来
* 给TeamLeader下指令
* 查一下数量
*
*/
boss.commandCheckNumber(teamLeader);
}
}
Course是由TeamLeade来生成的,Course不再和Boss发生接触,Boss也不需要知道Course,改造后通过类图,
就非常明显了,对于我们的大老板给TeamLeader下指令之后,你查完告诉我结果就可以了,不要让我关心那么多细节,
那迪米特法则理解起来也相当容易,最重要的是我们要分清楚哪些类是直接的朋友,哪些类不是朋友,只要能区分这个,
那我们在遵循迪米特法则进行开发的时候,也就游刃有余了,希望通过学习迪米特法则,在我们日常的工作和学习中呢,
都可以适当的利用起来