设计模式六大原则——迪米特法则
概念
- (Law of Demeter)又叫作最少知道原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD.
- 迪米特法则可以简单说成:talk only to your immediate friends。 对于OOD来说,又被解释为下面几种方式:一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
问题描述
类A依赖类B,但是类B中很多属性和方法是对类A开放的,这样类A在实现功能的时候,有可能会访问到类B中和类A无关的属性和方法。 这样在类B出现问题需要修改的时候,由于类A和类B之间耦合度太高,到这类A功能瘫痪。
问题由来
类与类之间关系过于紧密,耦合度太大,导致当一个类发生变化的时候,所有的类都要跟着改变。
解决方法
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。迪米特法则不希望类与类发生直接的通信,如果两个类不彼此直接通信,那么就需要一个第三者来转发。那么适合做第三者的条件如下:
- 当前对象本身。
- 以参量形式传入到当前对象方法中的对象
- 当前对象的实例变量直接引用的对象
- 当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友
- 当前对象所创建的对象
实例
场景:某公司为了各种目的,每年都将员工的绩效统计起来,到了年底的时候,依据绩效来划分等级,根据等级来发放奖金。用代码来描述这个场景。
//未遵守迪米特法则的实现方法
class BonusSystem
{
public:
void Method1()
{
cout << "依据策略一来发放奖金" << endl;
}
void Method2()
{
cout << "依据策略二来发放奖金" << endl;
}
void Method3()
{
cout << "依据策略三来发放奖金" << endl;
}
float GetPerformance()
{
return m_fPerformance;
}
private:
float m_fPerformance;
};
//客户端,程序只为表达意思,new BonusSystem省略。
class Client
{
public:
void GiveBonus()
{
float fPerformance = m_bonusSystem->GetPerformance();
if (fPerformance>9)
{
fPerformance->Method1();
}
else if (fPerformance > 7 && fPerformance<= 9)
{
fPerformance->Method2();
}
if (fPerformance > 5 && fPerformance<= 7)
{
fPerformance->Method3();
}
}
private:
BonusSystem* m_bonusSystem;
};
系统这么跑的好好的,但是突然有一天公司政策变了,现在多了两个等级,要求发放奖金的方式也多两种方式。那这个时候一看这代码,有点眼晕,不仅BonusSystem里面的方法要加,连客户端的代码也要改。这显然是有问题的,问题的本质就是两个类耦合太高。客户端只需要管发放奖金即可,至于奖金该如何发放,应该是属于BonusSystem来处理的。所以修改后的代码为:
class BonusSystem
{
public:
void Method1()
{
cout << "依据策略一来发放奖金" << endl;
}
void Method2()
{
cout << "依据策略二来发放奖金" << endl;
}
void Method3()
{
cout << "依据策略三来发放奖金" << endl;
}
void GiveBonus()
{
if (m_fPerformance > 9)
{
Method1();
}
else if (m_fPerformance > 7 && m_fPerformance<= 9)
{
Method2();
}
if (m_fPerformance > 5 && m_fPerformance<= 7)
{
Method3();
}
}
private:
float m_fPerformance;
};
//客户端,程序只为表达意思,new BonusSystem省略。
class Client
{
public:
void GiveBonus()
{
m_bonusSystem->GiveBonus();
}
private:
BonusSystem* m_bonusSystem;
};
如此实现,那不管以后方式如何变,客户端的代码不需要维护。这个例子其实很简单,大部分的人遇到这类需求也会潜意识里用第二种方法,不过这样举例只为表达思想,可能会有别的比较模糊的需求让我们在实现的时候不知道该怎么设计。所以在设计的时候可以参考这么一个原则:如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,就放置在本类中。
总结
- 迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。
- 在类设计的时候尽量降低一个类的访问权限。