设计模式(六)------设计模式六大原则(5):迪米特法则

转载自:http://www.jianshu.com/p/14589fb6978e (作者简书:涅槃1992)

揭秘迪米特法则

迪米特法则(Law of demeter,缩写是LOD)要求:一个对象应该对其他对象保持最少了解, 通缩的讲就是一个类对自己依赖的类知道的越少越好,也就是对于被依赖的类,向外公开的方法应该尽可能的少。

迪米特法则还有一种解释:Only talk to your immediate friends,即只与直接朋友通信.首先来解释编程中的朋友:两个对象之间的耦合关系称之为朋友,通常有依赖,关联,聚合和组成等.而直接朋友则通常表现为关联,聚合和组成关系,即两个对象之间联系更为紧密,通常以成员变量,方法的参数和返回值的形式出现.

那么为什么说是要与直接朋友通信呢?观察直接朋友出现的地方,我们发现在直接朋友出现的地方,大部分情况下可以接口或者父类来代替,可以增加灵活性. (需要注意,在考虑这个问题的时候,我们只考虑新增的类,而忽视java为我们提供的基础类.)

实例演示

不难发现,迪米特法则强调了一下两点:

  • 第一要义:从被依赖者的角度来说:只暴露应该暴露的方法或者属性,即在编写相关的类的时候确定方法/属性的权限
  • 第二要义:从依赖者的角度来说,只依赖应该依赖的对象

先来解释第一点,我们使用计算机来说明,以关闭计算机为例:

当我们按下计算机的关机按钮的时候,计算机会执行一些列的动作会被执行:比如保存当前未完成的任务,然后是关闭相关的服务,接着是关闭显示器,最后是关闭电源,这一系列的操作以此完成后,计算机才会正式被关闭。

现在,我们来用简单的代码表示这个过程,在不考虑迪米特法则情况下,我们可能写出以下代码

//计算机类
public class Computer{public void saveCurrentTask(){//do something}public void closeService(){//do something}public void closeScreen(){//do something}public void closePower(){//do something}public void close(){saveCurrentTask();closeService();closeScreen();closePower();}
}//人
public class Person{private Computer c;...public void clickCloseButton(){//现在你要开始关闭计算机了,正常来说你只需要调用close()方法即可,//但是你发现Computer所有的方法都是公开的,该怎么关闭呢?于是你写下了以下关闭的流程:        c.saveCurrentTask();c.closePower();c.close();//亦或是以下的操作        c.closePower();//还可能是以下的操作c.close();c.closePower();}}

发现上面的代码中的问题了没?
我们观察clickCloseButton()方法,我们发现这个方法无法编写:c是一个完全暴露的对象,其方法是完全公开的,那么对于Person来说,当他想要执行关闭的时候,却发现不知道该怎么操作:该调用什么方法?靠运气猜么?如果Person的对象是个不按常理出牌的,那这个Computer的对象岂不是要被搞坏么?

迪米特法则第一要义

现在我们来看看迪米特法则的第一点:从被依赖者的角度,只应该暴露应该暴露的方法。那么这里的c对象应该哪些方法应该是被暴露的呢?很显然,对于Person来说,只需要关注计算机的关闭操作,而不关心计算机会如何处理这个关闭操作,因此只需要暴露close()方法即可。
那么上述的代码应该被修改为:

//计算机类
public class Computer{private void saveCurrentTask(){//do something}private void closeService(){//do something}private void closeScreen(){//do something}private void closePower(){//do something}public void close(){saveCurrentTask();closeService();closeScreen();closePower();}
}//人
public class Person{private Computer c;...public  void clickCloseButton(){c.close();}}

看一下它的类图:


接下来,我们继续来看迪米特法则的第二层含义:从依赖者的角度来说,只依赖应该依赖的对象。 这句话令人有点困惑,什么叫做应该依赖的对象呢?我们还是用上面“关闭计算机”的例子来说明: 准确的说,计算机包括操作系统和相关硬件,我们可以将其划分为System对象和Container对象。当我们关闭计算机的时候,本质上是向操作系统发出了关机指令,而实则我们只是按了一下关机按钮,也就是我们并没有依赖System的对象,而是依赖了Container。这里Container就是我们上面所说的直接朋友---只和直接朋友通信.

我们就这点,继续深入讨论一下: only talk to your immedate friends 这句话只说明了要和直接朋友通信,但是我觉得这还不完整,我更愿意将其补充为: make sure your friends,only talk to your immedate friends,don't speak to strangers. 大意是:确定你真正的朋友,并只和他们通信,并且不要和陌生人讲话.这样做有个很大的好处就是,能够简化对象与对象之间的通信,进而减轻依赖,提供更高的灵活性,当然也可以提供一定的安全性.

现在来想想现实世界中的这么一种情况:你是一个令人瞩目的公众人物,周围的每个人都知道你的名字,当你独自走在大街上的时候会是怎么样的一种场景?每个人都想要和你聊天!,和你交换信息!!接着,你发现自己已经寸步难行了.如果这时候你有一个经纪人,来帮你应对周围的人,而你就只和这个经纪人通信,这样就大大减轻了你的压力,不是么?此时,这个经济人就相当于你的直接朋友.


迪米特法则第二要义

现在,我们再回顾"关机计算机"这个操作,前面的代码只是遵从了"暴漏应该暴漏的方法"这一点,现在我们结合第二点来进行改进:System和Container相比,System并非Person的直接朋友,而Container才是(Person直接打交道的是Container).因此我们需要将原有的Computer拆分成System和Cotainer,然后使Person只与Container通信,因此代码修改为:

//操作系统
public class System{private void saveCurrentTask(){//do something}private void closeService(){//do something}private void closeScreen(){//do something}private void closePower(){//do something}public void close(){saveCurrentTask();closeService();closeScreen();closePower();}
}//硬件设备容器
public class Container{private System mSystem;public void sendCloseCommand(){mSystem.close();}
}//人
ublic class Person{private Container c;....public void clickCloseButton(){c.sendCloseCommand();}}

来看一下它的类图:



对比这两种方案,明显这种方案二的解耦程度更高,灵活大大增强.不难发现,这应用了我们前面提到的依赖倒置,即面向接口编程.

除此之外,我们发现随着不断的改进,类的数量也在不断的增加,从2个增加到5个,这意味着为了解耦和提高灵活性通常要编写的类的数量会翻倍.因此,你需要在这做一个权衡,切莫刻意为了追求设计,而导致整个系统非常的冗余,最终可能得不偿失.


总结

有人会觉得Container像是一个中介(代理).没错,我们确实可以称其为中介,但这并不能否认他是我们的直接朋友:在很多情况下,中介可以说是我们的一种代表,因此将其定义为直接朋友是没有任何问题的.比如,当你想要租房的时候,你可以找房屋中介,对方会按照你的标准为你寻找合适的住房.但是问题来了:那么做一件事情需要多少中介呢?总不能是我委托一个中介A帮我找房子,但中介A又委托了中介B,中介B又委托了中介C....等等,如果真的是这样,那还不如我自己去找房子效率更高.在实际开发中,委托的层次要控制在6层以下,多余6层以上的会使得系统过分的冗余和并切会委托层次过多而导致开发人员无法正确的理解流程,产生风险的可能会大大提高.

到目前,我们已经彻底的了解了迪米特法则.



转载于:https://www.cnblogs.com/chz-blogs/p/9380982.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

http://www.cda.cn/view/25735.html

通过实例浅析Python对比C语言的编程思想差异 我一直使用 Python,用它处理各种数据科学项目。 Python 以易用闻名。有编码经验者学习数天就能上手(或有效使用它)。 听起来很不错,不过,如果你既用 Python,同时…

前端知识点梳理(一)

一、HTML 1. meta标签 记住2个属性&#xff1a;name和http-equiv name&#xff1a;描述网页 <meta name"参数" content"具体的描述">http-equiv&#xff1a;文件头 HTML中的meta标签及其使用方法 二、CSS 1. css实现水平居中的几种方式 css实…

Babel 7 基础入门学习(详细版)

可以在我的GitHub上下载示例代码。 前言 之前一直想要系统的学习一下Babel的使用规则&#xff0c;看过阮一峰老师的《Babel基础入门》&#xff0c;无奈此教程是2016年出的&#xff0c;而Babel 7都已经出来啦&#xff0c;于是&#xff0c;在搜集了各种资料后&#xff0c;关于…

Kubernetes学习之路(四)之Node节点二进制部署

K8S Node节点部署 1、部署kubelet &#xff08;1&#xff09;二进制包准备 [rootlinux-node1 ~]# cd /usr/local/src/kubernetes/server/bin/ [rootlinux-node1 bin]# cp kubelet kube-proxy /opt/kubernetes/bin/ [rootlinux-node1 bin]# scp kubelet kube-proxy 192.168.56.1…

前端知识点梳理(二)

1.内核 浏览器内核&#xff08;Rendering Engine&#xff09;最初分为&#xff1a;渲染引擎&#xff08;layout engineer&#xff09;或&#xff08;Rendering Engine&#xff09;和js引擎&#xff1b;后来 JS 引擎越来越独立&#xff0c;内核就倾向于单指渲染引擎。浏览器she…

微信小程序模仿开眼视频app(三)——信息卡片瀑布流和分类

《微信小程序模仿开眼视频app&#xff08;一&#xff09;——视频首页、视频详情、分类》 《微信小程序模仿开眼视频app&#xff08;二&#xff09;——搜索功能》 可到我的github账号上去copy文件 瀑布流部分 文件代码提示的挺详细的&#xff0c;这里主要点一下 社区与分类…

javascript --- typeof方法和instanceof方法

ES5中: 原始类型包括:Number、String、Boolean、Null、Undefined 原始封装类型包括:Number、String、Boolean 引用类型包括:Array、Function、RegExp、Error、Date、Error 变量对象 原始类型的实例成为原始值,它直接保存在变量对象中. 引用类型的实例成为引用值,它作为一个指针…

《JavaScript 高级程序设计》笔记 第1~5章

第1章 js是专为网页交互而设计的脚本语言&#xff0c;由3部分组成&#xff1a; ECMAScript&#xff0c;提供核心语言功能DOM文档对象模型&#xff0c;提供访问和操作网页内容的方法和接口BOM浏览器对象模型&#xff0c;提供与浏览器交互的方法和接口 js是一种脚本语言、解释…

javascript --- js中prototype、__proto__、[[Propto]]、constructor的关系

首先看下面一行代码: function Person(name){this.name name; } var person1 new Person; console.log(person1.__proto__ Person.prototype); console.log(person1.constructor Person);控制台打印如下: 可以看见,当使用构造函数(Person)构造一个实例(person1)时, 在后…

前端知识点整理收集(不定时更新~)

知识点都是搜集各种大佬们的&#xff0c;如有冒犯&#xff0c;请告知&#xff01; 目录 原型链 New关键字的执行过程 ES6——class constructor方法 类的实例对象 不存在变量提升 super 关键字 ES6——...&#xff08;展开/收集&#xff09;运算符 面向对象的理解 关…

重学《JavaScript 高级程序设计》笔记 第6章对象

第6章 面向对象的程序设计 ECMAScript中没有类的概念&#xff1b; 1.创建对象-历史 1.1 创建实例&#xff0c;添加方法和属性 → 对象字面量 缺点&#xff1a; 使用同一接口创建很多对象&#xff0c;产生大量重复代码 var person new Object() person.name "Y" pe…

javascrip --- 构造函数的继承

两点需要注意的. 第一是在构造函数声明时,会同时创建一个该构造函数的原型对象,而该原型对象是继承自Object的原型对象 // 声明一个构造函数Rectengle function Rectangle(length, width) {this.length length;this.width width; }// 即:看见function 后面函数名是大写,一般…

javascript --- 使用语法糖class定义函数

本文讨论的是通过class声明的函数,有什么特点,或者说是指向了哪里. class A() {} // A是一个类// 要看class声明的函数指向哪里,只需将其[[Prototype]]属性打印到控制台,下面看看A和它的原型对象的指向 // 注:[[Prototype]]属性通过__proto__访问 console.log(A.__proto__…

前端知识点整理收集(不定时更新~)二

目录 require() 加载文件机制 线程和进程 线程 单线程 Nodejs的线程与进程 网络模型 初识 TCP 协议 三次握手 I/O I/O 先修知识 阻塞与非阻塞 I/O 同步与异步 I/O Git 基础命令 分支操作 修改远程仓库地址 远程分支获取最新的版本到本地 拉取远程仓库指定分支…

SpringBoot零基础入门指南--搭建Springboot然后能够在浏览器返回数据

File->new Project 修改默认包名&#xff0c;根据自己的喜好修改 选择初始化需要导入的包&#xff0c;尽量不要一开始就导入很多&#xff0c;特别是数据库&#xff0c;不然启动可能会有问题&#xff0c;创建好的目录如下&#xff1a; 配置文件写在application.properties下&…

JavaScript算法相关

1. 排序 1.1.冒泡排序 每一轮比较&#xff0c;从左至右交换相邻&#xff0c;每轮结束&#xff0c;最后一个为最大下一轮&#xff0c;需要比较的个数 - 1 j < len - i (范围动态缩小)共 len - 1 轮比较 function bubbleSort(arr) {var len arr.length;for (var i 1; i &…

数据结构基础知识

排序 参考&#xff1a;https://www.bilibili.com/video/av38482633/?spm_id_fromtrigger_reload 目录 排序 插入排序 直接插入排序 折半排序 希尔排序 ​ 交换排序 冒泡排序 快速排序 选择排序 堆排序 流量单位计算 什么是计数排序 复杂度分析&#xff1a; 什…

linux中安装软件,查看、卸载已安装软件方法

各种主流Linux发行版都采用了某种形式的包管理系统&#xff08;PMS&#xff09;来控制软件和库的安装。 软件包存储在服务器上&#xff0c;可以利用本地Linux系统上的PMS工具通过互联网访问。这些服务器称为仓库。 由于Linux发行版众多,目前还没有统一的PMS标准工具。 这里分别…

html5 --- 使用javascript脚本控制媒体播放

H5中的标签(<audio…/> 和 <video…/>)对于JS中的HTMLAudioElement对象和HTMLVideoElement对象 对象有以下几个方法: play(): 播放 pause(): 暂停播放 load(): 重新装载音频、视频 canPlayType(type): 判断该元素可播放type类型的音频、视频 下面是一个简单的音乐…

css3 --- 使用媒体查询进行响应式布局

css3引入media,可以根据设备特性进行不同的布局, 本文展示的是根据不同屏幕的宽度进行不同的布局,代码如下: <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><title> 针…