迪米特法则
迪米特法则(Law of Demeter,LoD)
核心思想:一个软件实体应当尽可能少的与其他实体发生相互作用。只和直接(亲近)的朋友说话,不和陌生人说话。
一个类对自己依赖的类知道的越少越好,只需要和直接的对象进行交互,而不用在乎这个对象的内部组成结构。
案例分析:
案例一:
如类 A 中有类B的对象;类 B 中有类 C 的对象,调用方有一个类 A 的对象 a,这时要访问 C 对象的属性,不要采用类似下面的写法:
a.getB().getC().getProperties()
而应该是:
a.getCProperties()
至于 getCProperties 怎么实现是类 A 要负责的事情,我只和我直接的对象 a 进行交互,不访问我不了解的对象。
案例二:
动物园内种类繁多,有鸟类馆、熊猫馆。假设某国外领导人来访华,参观动物园,他想知道动物园内叫“贝贝”大熊猫年龄多大,体重多少。他难道要先去调取熊猫馆的信息,再去查找叫“贝贝”的这只大熊猫,再去看他的信息吗?显然不用,他只要问一下动物园的馆长就可以了。动物园的馆长会告诉他所有需要的信息,因为他只认识动物园的馆长,并不了解动物园的内部结构,也不需要去了解。
zooAdmin.getPandaBeiBeiInfo()
案例三:
假设我们有一个学校系统,其中包括School、Teacher和Student类。在不遵循迪米特法则的设计中,如果想要让学校打印出所有学生的姓名,可能会设计成学校直接遍历老师和学生:
class Student {
private name: string
constructor(name: string) {
this.name = name
}
public getName(): string {
return this.name
}
}
class Teacher {
private students: Student[]
constructor(students: Student[]) {
this.students = students
}
public getStudents() {
return this.students
}
}
class School {
private teachers: Teacher[]
constructor(teachers: Teacher[]) {
this.teachers = teachers
}
public printAllStudentNames(): void {
this.teachers.forEach(teacher => {
teacher.getStudents().forEach(student => {
console.log(student.getName())
})
})
}
}
function test() {
// 示例使用
const students1 = [new Student('John'), new Student('Jane')]
const teacher1 = new Teacher(students1)
const students2 = [new Student('Mike'), new Student('Anna')]
const teacher2 = new Teacher(students2)
const school = new School([teacher1, teacher2])
school.printAllStudentNames()
}
test()
在这个设计中,School类直接依赖于Teacher和Student类的内部实现(例如,School需要知道老师是如何存储学生的,以及如何获取学生的信息)。这种设计违反了迪米特法则,因为School类需要与非直接的朋友Student类进行交互。
遵循迪米特法则的设计应该尽量减少School对Teacher和Student内部实现的了解。可以让Teacher类提供一个打印所有学生姓名的方法,然后School类只需要调用每个Teacher的这个方法:
class Student {
private name: string
constructor(name: string) {
this.name = name
}
public getName(): string {
return this.name
}
}
class Teacher {
private students: Student[]
constructor(students: Student[]) {
this.students = students
}
public printStudentNames(): void {
this.students.forEach(student => console.log(student.getName()))
}
}
class School {
private teachers: Teacher[]
constructor(teachers: Teacher[]) {
this.teachers = teachers
}
public printAllStudentNames(): void {
this.teachers.forEach(teacher => teacher.printStudentNames())
}
}
function test() {
// 示例使用
const students1 = [new Student('John'), new Student('Jane')]
const teacher1 = new Teacher(students1)
const students2 = [new Student('Mike'), new Student('Anna')]
const teacher2 = new Teacher(students2)
const school = new School([teacher1, teacher2])
school.printAllStudentNames()
}
test()