<article>
<div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post" style="height: 2256px; overflow: hidden;">
<div class="article-copyright">
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hfreeman2008/article/details/52335601 </div>
<div class="markdown_views">
<!-- flowchart 箭头图标 勿删 -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg>
<p><img src="https://img-blog.csdn.net/20160910124652162" alt="这里写图片描述" title=""></p>
<h1 id="迪米特原则的定义"><a name="t0"></a>迪米特原则的定义</h1>
<p>迪米特原则(Law of Demeter,LoD),也叫最少知识原则(Low knowledge Principle,LKP):</p>
<p><strong>一个对象应该对其他对象有最少的了解。</strong></p>
<p>通俗的讲:一个类对自己需要耦合或调用的类知道的最少,你(被耦合或调用的类)的内部是如何复杂和我没有关系,我就知道你提供的public方法,我只调用这些方法,其它的我不关心。</p>
<h1 id="迪米特原则的具体要求"><a name="t1"></a>迪米特原则的具体要求</h1>
<p>迪米特原则对类的低耦合提出了明确的要求:</p>
<h2 id="只与朋友类交流"><a name="t2"></a>只与朋友类交流</h2>
<p>迪米特原则还有一个解释:Only talk to your immediate friends(只与直接朋友通信)。 <br>
什么叫直接朋友呢?每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系类型有很多,例如:组合,聚合,依赖等。朋友类也可以这样定义:出现在成员变量,方法的输入输出参数中的类,称为朋友类。</p>
<p>上体育课,我们经常有这样一个场景: <br>
体育老师上课前要体育委员确认一下全班女生到了多少位,也就是体育委员清点女生的人数。类图如下:</p>
<p><img src="https://img-blog.csdn.net/20160828125451589" alt="这里写图片描述" title=""></p>
<p>老师类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Teacher{
<span class="hljs-comment">//老师对体育委员发一个命令,让其清点女生人数</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">command</span>(GroupLeader groupLeader){
List<Girl> listGirls = <span class="hljs-keyword">new</span> ArrayList();
<span class="hljs-comment">//初始化女生</span>
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i<<span class="hljs-number">20</span>;i++){
listGirls.add(<span class="hljs-keyword">new</span> Girl());
}
<span class="hljs-comment">//告诉体育委员开始清点女生人数</span>
groupLeader.countGirls(listGirls);
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li></ul></pre>
<p>体育委员类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> GroupLeader{
<span class="hljs-comment">//清点女生数量</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">countGirls</span>(List<Girl> listGirls){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"女生人数是:"</span>+listGirls.size());
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li></ul></pre>
<p>女生类:</p>
<pre class="prettyprint" name="code"><code class="hljs ruby has-numbering">publci <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Girl</span>{</span>
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li></ul></pre>
<p>场景类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Client{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(Strings[] args){
Teacher teacher = <span class="hljs-keyword">new</span> Teacher();
<span class="hljs-comment">//老师给体育委员发清点女生人数的命令</span>
teacher.command(<span class="hljs-keyword">new</span> GroupLeader());
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li></ul></pre>
<p>我们再回头看Teacher类,Teacher类只有一个朋友类GroupLeader,Girl类不是朋友类,但是Teacher与Girl类通信了,这就破坏了Teacher类的健壮性,Teacher类的方法竟然与一个不是自己的朋友类Girl类通信,这是不允许的,严重违反了迪米特原则。</p>
<p>我们对程序进行如下修改,将类图修改如下:</p>
<p><img src="https://img-blog.csdn.net/20160828125512620" alt="这里写图片描述" title=""></p>
<p>修改后的老师类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Teacher{
<span class="hljs-comment">//老师对体育委员发一个命令,让其清点女生人数</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">command</span>(GroupLeader groupLeader){
<span class="hljs-comment">//告诉体育委员开始清点女生人数</span>
groupLeader.countGirls();
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li></ul></pre>
<p>修改后的体育委员类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> GroupLeader{
<span class="hljs-keyword">private</span> List<Girl> listGirls;
<span class="hljs-keyword">public</span> <span class="hljs-title">GroupLeader</span>(List<Girl> listGirls){
<span class="hljs-keyword">this</span>.listGirls = listGirls;
}
<span class="hljs-comment">//清点女生数量</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">countGirls</span>(){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"女生人数是:"</span>+listGirls.size());
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li></ul></pre>
<p>修改后的场景类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Client{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(Strings[] args){
<span class="hljs-comment">//产生女生群体</span>
List<Girl> listGirls = <span class="hljs-keyword">new</span> ArrayList<Girl>();
<span class="hljs-comment">//初始化女生</span>
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>;i<<span class="hljs-number">20</span>;i++){
listGirls.add(<span class="hljs-keyword">new</span> Girl());
}
Teacher teacher = <span class="hljs-keyword">new</span> Teacher();
<span class="hljs-comment">//老师给体育委员发清点女生人数的命令</span>
teacher.command(<span class="hljs-keyword">new</span> GroupLeader(listGirls));
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li></ul></pre>
<p>对程序修改,把Teacher中对Girl群体的初始化移动到场景类中,同时在GroupLeader中增加对Girl的注入,避开了Teacher类对陌生类Girl的访问,降低了系统间的耦合,提高了系统的健壮性。</p>
<h2 id="朋友类间也是要有距离"><a name="t3"></a>朋友类间也是要有距离</h2>
<p>我们在安装软件时,经常会有一个安装向导的过程。比如第一步确认是否安装,第二步确认License,第三步选择安装目录…..。这个是一个典型的顺序执行动作,我们定义软件安装过程的类图如下:</p>
<p><img src="https://img-blog.csdn.net/20160828125527198" alt="这里写图片描述" title=""></p>
<p>导向类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Wizard{
<span class="hljs-keyword">private</span> Random rand = <span class="hljs-keyword">new</span> Random(System.currentTimeMillis());
<span class="hljs-comment">//第一步</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">first</span>(){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"执行第一个方法....."</span>);
<span class="hljs-keyword">return</span> rand.nextInt(<span class="hljs-number">100</span>);
}
<span class="hljs-comment">//第二步</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">second</span>(){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"执行第二个方法....."</span>);
<span class="hljs-keyword">return</span> rand.nextInt(<span class="hljs-number">100</span>);
}
<span class="hljs-comment">//第三步</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">third</span>(){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"执行第三个方法....."</span>);
<span class="hljs-keyword">return</span> rand.nextInt(<span class="hljs-number">100</span>);
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li></ul></pre>
<p>InstallSoftware类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> InstallSoftware{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">installWizard</span>(Wizard wizard){
<span class="hljs-keyword">int</span> first = wizard.first();
<span class="hljs-comment">//根据first返回的结果,看是否要执行下一步</span>
<span class="hljs-keyword">if</span>(first ><span class="hljs-number">50</span>){
<span class="hljs-keyword">int</span> second = wizard.second();
<span class="hljs-keyword">if</span>(second ><span class="hljs-number">50</span>){
wizard.third();
}
}
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li></ul></pre>
<p>场景类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Client{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(Strings[] args){
InstallSoftware invoker = <span class="hljs-keyword">new</span> InstallSoftware();
invoker.installWizard(<span class="hljs-keyword">new</span> Wizard());
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li></ul></pre>
<p>以上的程序非常简单,但是隐藏了一个问题。Wizard类把太多的方法暴露给InstallSoftware类,导致两者的关系太亲密,耦合关系变量异常牢固。我们把Wizard类进行重构,uml类图如下:</p>
<p><img src="https://img-blog.csdn.net/20160828125542058" alt="这里写图片描述" title=""></p>
<p>修改后的Wizard类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> Wizard{
<span class="hljs-keyword">private</span> Random rand = <span class="hljs-keyword">new</span> Random(System.currentTimeMillis());
<span class="hljs-comment">//第一步</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">first</span>(){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"执行第一个方法....."</span>);
<span class="hljs-keyword">return</span> rand.nextInt(<span class="hljs-number">100</span>);
}
<span class="hljs-comment">//第二步</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">second</span>(){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"执行第二个方法....."</span>);
<span class="hljs-keyword">return</span> rand.nextInt(<span class="hljs-number">100</span>);
}
<span class="hljs-comment">//第三步</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">third</span>(){
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"执行第三个方法....."</span>);
<span class="hljs-keyword">return</span> rand.nextInt(<span class="hljs-number">100</span>);
}
<span class="hljs-comment">//软件安装过程</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">installWizard</span>(){
<span class="hljs-keyword">int</span> first = wizard.first();
<span class="hljs-comment">//根据first返回的结果,看是否要执行下一步</span>
<span class="hljs-keyword">if</span>(first ><span class="hljs-number">50</span>){
<span class="hljs-keyword">int</span> second = wizard.second();
<span class="hljs-keyword">if</span>(second ><span class="hljs-number">50</span>){
wizard.third();
}
}
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li></ul></pre>
<p>修改后的InstallSoftware类:</p>
<pre class="prettyprint" name="code"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> InstallSoftware{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">installWizard</span>(Wizard wizard){
wizard.installWizard()
}
}</code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li></ul></pre>
<p>通过重构,类间的耦合关系变弱了,结构变得清晰,变量的风险也变小了。 <br>
一个类公开的public方法和属性越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此,为了保持朋友类间的距离,在设计时需要反复衡量:是否还可以再减少public方法和属性,是否可以修改为private,package-private,protected等访问权限,是否可以加上final关键字。</p>
<p>注意: <br>
<strong>迪米特原则要求类“羞涩”一点,尽量不要对外公开太多的public方法和非静态的public变量,尽量内敛,多使用private,package-private,protected等访问权限。</strong></p>
<h2 id="是自己的就是自己的"><a name="t4"></a>是自己的就是自己的</h2>
<p>在实践中经常出现这样一个方法,放在本类中也可以,放到其它类中也可以。那怎么处理呢?你可以坚持一个原则:<strong>如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,那就放到本类中。</strong></p>
<h1 id="迪米特原则的实践"><a name="t5"></a>迪米特原则的实践</h1>
<p>迪米特原则的核心观念就是类间解耦,弱耦合,只有弱耦合后,类的复用率才可以提高。其结果就是产生了大量的中转或跳转类,导致系统复杂,为维护带来了难度。所以,我们在实践时要反复权衡,即要让结构清晰,又做到高内聚低耦合。</p> </div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-7f770a53f2.css" rel="stylesheet">
</div>
<div class="hide-article-box text-center">
<a class="btn" id="btn-readmore" data-track-view="{"mod":"popu_376","con":",https://blog.csdn.net/hfreeman2008/article/details/52335601,"}" data-track-click="{"mod":"popu_376","con":",https://blog.csdn.net/hfreeman2008/article/details/52335601,"}">阅读更多</a>
</div>
<script>
(function(){
function setArticleH(btnReadmore,posi){
var winH = $(window).height();
var articleBox = $("div.article_content");
var artH = articleBox.height();
if(artH > winH*posi){
articleBox.css({
'height':winH*posi+'px',
'overflow':'hidden'
})
btnReadmore.click(function(){
articleBox.removeAttr("style");
$(this).parent().remove();
})
}else{
btnReadmore.parent().remove();
}
}
var btnReadmore = $("#btn-readmore");
if(btnReadmore.length>0){
if(currentUserName){
setArticleH(btnReadmore,3);
}else{
setArticleH(btnReadmore,1.2);
}
}
})()
</script>
</article>