一、概述
迪米特原则(
Law of Demeter
,缩写:LoD),又叫作最少知识原则(Least Knowledge Principle,简写LKP),就是说一个对象应当对其他对象有尽可能少的了解。
二、原则
一个类应该对自己需要耦合或调用的类知道的最少,类的内部如何实现与调用者或者依赖者没关系,调用者或者依赖者只需要知道它需要的方法即可,其他的可一概不用管。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
迪米特法则可以简单说成:talk only to your immediate friends.也就是“只与直接的朋友通信”。至于什么是直接的朋友?每个对象都必然会与其它对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有很多,如组合、聚合、依赖等。
三、举例说明
下面引用《Android源码设计模式解析与实战》一书中示例:
1.只要求房间的面积和租金,其他一概不管,中介将符合我要求的房子提供给我就可以,如下代码:
/*
* 房间
* */
public class Room{
public float area;
public float price;
public Room(float area, float price){
this.area = area;
this.price = price;
}
@Override
public String toString() {
return "Room [area="+area+",price="+price+"]";
}
}
/*
* 中介
* */
public class Mediator{
List<Room> mRooms = new ArrayList<Room>();
public Mediator(){
for (int i=0;i<5;i++){
mRooms.add(new Room(14+i,(14+i)*150));
}
}
public List<Room> getAllRooms(){
return mRooms;
}
}
/*
* 租户
* */
public class Tenant{
public float roomArea;
public float roomPrice;
public static final float diffPrice = 100.0001f;
public static final float diffArea = 0.00001f;
public void rentRoom(Mediator mediator){
List<Room> rooms = mediator.getAllRooms();
for (Room room:rooms){
if(isSuitable(room)) {
System.out.println("租到房啦!" + room);
break;
}
}
}
private boolean isSuitable(Room room){
return Math.abs(room.price - roomPrice) < diffPrice
&&Math.abs(room.area = roomArea) < diffArea;
}
}
2.从上述代码可看到,Tenant不仅依赖了Mediator类,还需要频繁地与Room类打交道,导致Tenant与Room的耦合较高,所以需要解耦。首先明确的是我们只和我们的朋友通信,这里指Mediator对象,必须将Room相关的操作从Tenant中移除,至此我们重构代码如下:
/*
* 房间
* */
public class Room{
public float area;
public float price;
public Room(float area, float price){
this.area = area;
this.price = price;
}
@Override
public String toString() {
return "Room [area="+area+",price="+price+"]";
}
}
/*
* 中介
* */
public class Mediator{
List<Room> mRooms = new ArrayList<Room>();
public Mediator(){
for (int i=0;i<5;i++){
mRooms.add(new Room(14+i,(14+i)*150));
}
}
public Room rentOut(float area, float price){
for (Room room : mRooms){
if (isSuitable(area , price ,room)){
return room;
}
}
return null;
}
private boolean isSuitable(float area, float price, Room room){
return Math.abs(room.price - price) < Tenant.diffPrice
&&Math.abs(room.area = area) < Tenant.diffArea;
}
}
/*
* 租户
* */
public class Tenant{
public float roomArea;
public float roomPrice;
public static final float diffPrice = 100.0001f;
public static final float diffArea = 0.00001f;
public void rentRoom(Mediator mediator){
System.out.print("租到房啦!"+mediator.rentOut(roomArea,roomPrice));
}
}
只是将对于Room的判定操作移到了Mediator类中,这本应该是Mediator的职责,根据租户设定的条件查找符合要求的房子,并且将结果交给租户就可以了。租户并不需要知道太多关于Room的细节,比如与房东签合同、设施坏了找谁维修等。当我们通过我们的“朋友”--中介 租了房后,所有的事情我们都通过与中介沟通就好了,房东、维修师傅等角色都并不是我们直接的“朋友”。
“只与直接的朋友通信”这就足够将我们从复杂的关系网中抽离出来,使程序耦合度更低、稳定性更好。
四、分析
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系--这在一定程度上增加了系统的复杂度。