设计模式六大原则之--迪米特法则(LoD)
2014-04-18 15:14
513 查看
1.迪米特法则:Law of Demeter, LoD),也称最少知识原则(Least Knowledge Principle, LKP)
定义:Only talk to your immedate friends.(只与直接的朋友通信)。一个对象应该对其他对象有最少的了解。(通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少)
2.理解:
2.1 只和朋友交流(更准确来讲是:直接的朋友)
每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有很多,如组合、聚合、依赖等。
朋友类的定义:出现在成员变量、方法的输入输出参数中的类。 而方法体类内部的类不能算。
2.2 朋友之间也有间距
如果朋友把太多的方法或属性暴露给你,则过于亲密,耦合关系变得异常牢固,而且,修改时涉及的面也就越大,变更引起的风险就越大。因此,要适时反复衡量:是否可以减少public方法和属性,改为private、package-private、protected等访问权限,及是否可以加上final关键字。
3.问题由来:
类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。[解决方案]尽量降低类与类之间的耦合。
4.使用LoD的好处:
降低类与类之间的耦合。
5.难点:
LoD法则的核心就是类间解耦,弱耦合,但实现过程则是通过“朋友类”来中转,结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,增加了维护的难度。一个准则:只要跳转不超过两次都是可以忍受的,反之则要考虑重构;
LoD法则要求解耦,但解耦是有限度的,原则仅供参考,严格执行就是“过犹不及”。
6.最佳实践:
① 在类的划分上,应该创建有弱耦合的类;
② 在类的结构设计上,每一个类都应当尽量降低成员的访问权限;
③ 在类的设计上,只要有可能,一个类应当设计成不变类;
④ 在对其他类的引用上,一个对象对其它对象的引用应当降到最低;
⑤ 尽量降低类的访问权限;
⑥ 谨慎使用序列化功能(类或接口在客户端变更,却未在服务端同步更新,引发序列化失败,,项目管理易疏忽);
⑦ 不要暴露类成员,而应该提供相应的访问器(属性)。
在实际应用中经常会出现这样一个方法:放在本类中也可以,放在其他类中也没有问题,怎么去衡量呢?建议:如果一个类放在本类中,既不增加类间关系,又不对本类产生不负面影响,就放置在本类中。
7.范例:
7.1 一个常态的编程(肯定是不符LoD的反例)
//体育老老师让体委清点全班女生个数。类图如下:
对应源代码如下:
看看上面的要求:老师 让 体委 清点 女生 数目。老师与女生是陌生关系啊(老师不需要女生执行任何动作)。显然,上述做法违背LoD法则。
改如下:
7.2 依据LoD法则解耦(与真实意义上的陌生类解耦,这里而不应为上述类图中未正确体会语义的虚假朋友类Girl):
对应源码如下:
7.3 朋友间不要过于亲密,太亲密则过于耦合。(根据情况,缩减访问控制域)
//安装软件向导示例:
源代码如下://原始代码
经观察发现,Wizard 类把太多的方法暴露给InstallSoftware类,两者朋友关系过于亲密了,耦合关系变得异常牢固。改为如下:
//源代码如下:
7.4 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另外一个类的某一个方法,可以通过第三者转发这个调用。参考下例,Someone、Friend和Stranger三个类。
在这里,Someone类和Stranger类不是朋友类,但Someone类却通过Friend类知道了Stranger类的存在,这显然违反迪米特法则。现在,我们对Someone和Friend类进行重构。首先在Friend类里添加一个方法,封装对Stranger类的操作:
现在Someone对Stranger的依赖完全通过Friend隔离,这样的结构已经符合狭义迪米特法则了。仔细观察上述结构,会发现狭义迪米特法则一个明显的缺点:会在系统里造出大量的小方法,散落在系统的各个角落。这些方法仅仅是传递间接的调用,因此与系统的商务逻辑无关,当设计师试图从一张类图看出总体的框架时,这些小的方法会造成迷惑和困扰。遵循迪米特法则会使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接关联。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。
结合依赖倒转原则,我们对代码进行如下重构来解决这个问题,首先添加一个抽象的Stranger类,使Someone依赖于抽象的“Stranger”角色,而不是具体实现:
public abstract class AbstractStranger{
abstract void operation3();
}
//然后,让Stranger从该类继承:
public class Stranger extends AbstractStranger{
public void operation3(){}
}
//随后,我们重构Someone使其依赖抽象的Stranger角色:
public class Someone{
public void operation1(Friend friend){
AbstractStranger stranger = friend.provide();
stranger.operation3();
}
}
//最后,我们重构Friend的provide方法,使其返回抽象角色:
public class Friend{
private Stranger stranger = new Stranger();
public void operation2(){}
public AbstractStranger provide(){
return stranger ;
}
}
现在,AbstractStranger成为Someone的朋友类,而Friend类可以随时替换掉AbstractStranger的实现类,Someone不再需要了解Stranger的内部实现细节。下图是重构后的UML类图:
转自:/article/2368988.html
定义:Only talk to your immedate friends.(只与直接的朋友通信)。一个对象应该对其他对象有最少的了解。(通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少)
2.理解:
2.1 只和朋友交流(更准确来讲是:直接的朋友)
每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有很多,如组合、聚合、依赖等。
朋友类的定义:出现在成员变量、方法的输入输出参数中的类。 而方法体类内部的类不能算。
2.2 朋友之间也有间距
如果朋友把太多的方法或属性暴露给你,则过于亲密,耦合关系变得异常牢固,而且,修改时涉及的面也就越大,变更引起的风险就越大。因此,要适时反复衡量:是否可以减少public方法和属性,改为private、package-private、protected等访问权限,及是否可以加上final关键字。
3.问题由来:
类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。[解决方案]尽量降低类与类之间的耦合。
4.使用LoD的好处:
降低类与类之间的耦合。
5.难点:
LoD法则的核心就是类间解耦,弱耦合,但实现过程则是通过“朋友类”来中转,结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,增加了维护的难度。一个准则:只要跳转不超过两次都是可以忍受的,反之则要考虑重构;
LoD法则要求解耦,但解耦是有限度的,原则仅供参考,严格执行就是“过犹不及”。
6.最佳实践:
① 在类的划分上,应该创建有弱耦合的类;
② 在类的结构设计上,每一个类都应当尽量降低成员的访问权限;
③ 在类的设计上,只要有可能,一个类应当设计成不变类;
④ 在对其他类的引用上,一个对象对其它对象的引用应当降到最低;
⑤ 尽量降低类的访问权限;
⑥ 谨慎使用序列化功能(类或接口在客户端变更,却未在服务端同步更新,引发序列化失败,,项目管理易疏忽);
⑦ 不要暴露类成员,而应该提供相应的访问器(属性)。
在实际应用中经常会出现这样一个方法:放在本类中也可以,放在其他类中也没有问题,怎么去衡量呢?建议:如果一个类放在本类中,既不增加类间关系,又不对本类产生不负面影响,就放置在本类中。
7.范例:
7.1 一个常态的编程(肯定是不符LoD的反例)
//体育老老师让体委清点全班女生个数。类图如下:
对应源代码如下:
public class Teacher { //老师对学生发布命令,清一下女生 public void commond(GroupLeader groupLeader){ List<Girl> listGirls = new ArrayList(); //初始化女生 for(int i=0;i<20;i++){ listGirls.add(new Girl()); } //告诉体育委员开始执行清查任务 groupLeader.countGirls(listGirls); } } public class GroupLeader { //有清查女生的工作 public void countGirls(List<Girl> listGirls){ System.out.println("女生数量是:"+listGirls.size()); } } public class Girl { } public class Client { public static void main(String[] args) { Teacher teacher= new Teacher(); //老师发布命令 teacher.commond(new GroupLeader()); } }
看看上面的要求:老师 让 体委 清点 女生 数目。老师与女生是陌生关系啊(老师不需要女生执行任何动作)。显然,上述做法违背LoD法则。
改如下:
7.2 依据LoD法则解耦(与真实意义上的陌生类解耦,这里而不应为上述类图中未正确体会语义的虚假朋友类Girl):
对应源码如下:
public class Teacher { //老师对学生发布命令,清一下女生 public void commond(GroupLeader groupLeader){ //告诉体育委员开始执行清查任务 groupLeader.countGirls(); } } public class GroupLeader { private List<Girl> listGirls; //传递全班的女生 public GroupLeader(List<Girl> _listGirls){ this.listGirls = _listGirls; } //有清查女生的工作 public void countGirls(){ System.out.println("女生数量是:"+listGirls.size()); } } public class Girl { } public class Client { public static void main(String[] args) { List<Girl> listGirls = new ArrayList<Girl>(); //初始化女生 for(int i=0;i<20;i++){ listGirls.add(new Girl()); } Teacher teacher= new Teacher(); //老师发布命令 teacher.commond(new GroupLeader(listGirls)); } }
7.3 朋友间不要过于亲密,太亲密则过于耦合。(根据情况,缩减访问控制域)
//安装软件向导示例:
源代码如下://原始代码
public class Wizard { private Random rand = new Random(System.currentTimeMillis()); //第一步 public int first(){ System.out.println("执行第一个方法..."); return rand.nextInt(100); } //第二步 public int second(){ System.out.println("执行第二个方法..."); return rand.nextInt(100); } //第三个方法 public int third(){ System.out.println("执行第三个方法..."); return rand.nextInt(100); } } public class InstallSoftware { public void installWizard(Wizard wizard){ int first = wizard.first(); //根据first返回的结果,看是否需要执行second if(first>50){ int second = wizard.second(); if(second>50){ int third = wizard.third(); if(third >50){ wizard.first(); } } } } } public class Client { public static void main(String[] args) { InstallSoftware invoker = new InstallSoftware(); invoker.installWizard(new Wizard()); } }
经观察发现,Wizard 类把太多的方法暴露给InstallSoftware类,两者朋友关系过于亲密了,耦合关系变得异常牢固。改为如下:
//源代码如下:
public class Wizard { private Random rand = new Random(System.currentTimeMillis()); //第一步 private int first(){ System.out.println("执行第一个方法..."); return rand.nextInt(100); } //第二步 private int second(){ System.out.println("执行第二个方法..."); return rand.nextInt(100); } //第三个方法 private int third(){ System.out.println("执行第三个方法..."); return rand.nextInt(100); } //软件安装过程 public void installWizard(){ int first = this.first(); //根据first返回的结果,看是否需要执行second if(first>50){ int second = this.second(); if(second>50){ int third = this.third(); if(third >50){ this.first(); } } } } } public class InstallSoftware { public void installWizard(Wizard wizard){ //不废话,直接调用 wizard.installWizard(); } } public class Client { public static void main(String[] args) { InstallSoftware invoker = new InstallSoftware(); invoker.installWizard(new Wizard()); } }
7.4 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另外一个类的某一个方法,可以通过第三者转发这个调用。参考下例,Someone、Friend和Stranger三个类。
//Someone类有一个方法接受一个Friend类型的变量: public class Someone{ public void operation1( Friend friend ){ Stranger stranger = friend.provide(); stranger.operation3() ; } } //所以Someone和Friend是朋友类(直接通讯的类)。同理,Friend类持有一个Stranger类的私有对象,他们是朋友类: public class Friend{ private Stranger stranger = new Stranger(); public void operation2(){ } public Stranger provide(){ return stranger; } }
在这里,Someone类和Stranger类不是朋友类,但Someone类却通过Friend类知道了Stranger类的存在,这显然违反迪米特法则。现在,我们对Someone和Friend类进行重构。首先在Friend类里添加一个方法,封装对Stranger类的操作:
public class Friend{ private Stranger stranger = new Stranger(); public void operation2(){} public Stranger provide(){ return stranger ; } public void forward(){ stranger.operation3(); } } //然后,我们重构Someone的operation1方法,让其调用新提供的forward方法: public class Someone{ public void operation1(Friend friend){ friend.forward(); } }
现在Someone对Stranger的依赖完全通过Friend隔离,这样的结构已经符合狭义迪米特法则了。仔细观察上述结构,会发现狭义迪米特法则一个明显的缺点:会在系统里造出大量的小方法,散落在系统的各个角落。这些方法仅仅是传递间接的调用,因此与系统的商务逻辑无关,当设计师试图从一张类图看出总体的框架时,这些小的方法会造成迷惑和困扰。遵循迪米特法则会使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接关联。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。
结合依赖倒转原则,我们对代码进行如下重构来解决这个问题,首先添加一个抽象的Stranger类,使Someone依赖于抽象的“Stranger”角色,而不是具体实现:
public abstract class AbstractStranger{
abstract void operation3();
}
//然后,让Stranger从该类继承:
public class Stranger extends AbstractStranger{
public void operation3(){}
}
//随后,我们重构Someone使其依赖抽象的Stranger角色:
public class Someone{
public void operation1(Friend friend){
AbstractStranger stranger = friend.provide();
stranger.operation3();
}
}
//最后,我们重构Friend的provide方法,使其返回抽象角色:
public class Friend{
private Stranger stranger = new Stranger();
public void operation2(){}
public AbstractStranger provide(){
return stranger ;
}
}
现在,AbstractStranger成为Someone的朋友类,而Friend类可以随时替换掉AbstractStranger的实现类,Someone不再需要了解Stranger的内部实现细节。下图是重构后的UML类图:
转自:/article/2368988.html
相关文章推荐
- 设计模式之六大原则——迪米特法则(LoD,LKP)
- 设计模式六大原则——迪米特法则(LoD)
- 设计模式六大原则(五)-- 最少知识原则 ( LKP ) / 迪米特法则 ( LOD )
- IOS设计模式的六大设计原则之迪米特法则(LOD,Law Of Demeter)
- 设计模式六大原则(5):迪米特法则LOD(Law Of Demeter)
- IOS设计模式的六大设计原则之迪米特法则(LOD,Law Of Demeter)
- 设计模式值六大原则——迪米特法则(LoD)也称为最少知识原则(LKP)。
- 设计模式六大原则例子(五)-- 最少知识原则(LKP)/ 迪米特法则(LOD)例子
- 设计模式之六大原则——迪米特法则(LoD,LKP)
- 设计模式之六大原则——迪米特法则(LoD,LKP)
- 设计模式六大原则——迪米特法则(LoD)
- [置顶] 设计模式之六大原则——迪米特法则(LoD,LKP)
- 设计模式之六大原则——迪米特法则(LoD,LKP)
- 设计模式六大原则——迪米特法则(LoD,Law of Demeter)
- IOS设计模式的六大设计原则之迪米特法则(LOD,Law Of Demeter)
- 设计模式六大原则——迪米特法则(LoD)
- 设计模式之六大原则——迪米特法则(LoD,LKP)
- IOS设计模式的六大设计原则之迪米特法则(LOD,Law Of Demeter)
- 设计模式之六大原则——迪米特法则(LoD,LKP)
- 设计模式之六大原则——迪米特法则(LoD,LKP)