面向对象葵花宝典:思想、技巧与实践(29) - 高内聚低耦合

简介: l转自博客: http://blog.csdn.net/yunhua_lee/article/details/25074707 高内聚低耦合,可以说是每个程序猿,甚至是编过程序,或者仅仅只是在大学里面学过计算机,都知道的一个简单的设计原则。

l转自博客:
http://blog.csdn.net/yunhua_lee/article/details/25074707

高内聚低耦合
,可以说是每个程序猿,甚至是编过程序,或者仅仅只是在大学里面学过计算机,都知道的一个简单的设计原则。

虽然如此流行和人所众知,但其实真正理解的人并不多,很多时候都是人云亦云


===============================================================

要想真正理解“高内聚低耦合”,需要回答两个问题:

1)为什么要高内聚低耦合?

2)高内聚低耦合是否意味内聚越高越好,耦合越低越好?

 

第一个问题:为什么要高内聚低耦合?

经典的回答是:降低复杂性。

确实很经典,当然,其实也是废话!我相信大部分人看了后还是不懂,什么叫复杂性呢?

 

要回答这个问题,其实可以采用逆向思维,即:如果我们不做到这点,将会怎样?

首先来看内聚,试想一下,假如我们是低内聚,情况将会如何?

 

前面我们在阐述内聚的时候提到内聚的关键在于“元素的凝聚力”,如果内聚性低,则说明凝聚力低;对于一个团队来说,如果凝聚力低,则一个明显的问题是“不稳定”;对于一个模块来说,内聚性低的问题也是一样的“不稳定”。具体来说就是如果一个模块内聚性较低,则这个模块很容易变化。一旦变化,设计、编码、测试、编译、部署的工作量就上来了,而一旦一个模块变化,与之相关的模块都需要跟着改变。

 

举一个简单的例子,假设有这样一个设计不好的类:Person,其同时具有“学生”、“运动员”、“演员”3个职责,有另外3个类“老师”、“教练”、“导演”依赖这个类。

Person.java


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.cohesion.low;  
  2.   
  3. /** 
  4.  * “人”的类设计 
  5.  * 
  6.  */  
  7. public class Person {  
  8.   
  9.     /** 
  10.      * 学生的职责:学习 
  11.      */  
  12.     public void study() {  
  13.         //TODO: student's responsibility  
  14.     }  
  15.       
  16.     /** 
  17.      * 运动员的职责:运动 
  18.      */  
  19.     public void play(){  
  20.         //TODO: sportsman's responsibility  
  21.     }  
  22.       
  23.     /** 
  24.      * 演员的职责:扮演 
  25.      */  
  26.     public void act(){  
  27.         //TODO: actor's responsibity  
  28.     }  
  29. }  


Teacher.java


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.cohesion.low;  
  2.   
  3. /** 
  4.  * “老师”的类设计 
  5.  * 
  6.  */  
  7. public class Teacher {  
  8.   
  9.     public void teach(Person student){  
  10.         student.study(); //依赖Person类的“学生”相关的职责  
  11.     }  
  12. }  


Coach.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.cohesion.low;  
  2.   
  3. /** 
  4.  * “教练”的类设计 
  5.  * 
  6.  */  
  7. public class Coach {  
  8.       
  9.     public void train(Person trainee){  
  10.         trainee.play();  //依赖Person类的“运动员”职责  
  11.     }  
  12. }  


Director.java


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.cohesion.low;  
  2.   
  3. /** 
  4.  * “导演”的类设计 
  5.  * 
  6.  */  
  7.   
  8. public class Director {  
  9.   
  10.     public void direct(Person actor){  
  11.         actor.act(); //依赖Person类“演员”的相关职责  
  12.     }  
  13. }  



在上面的样例中,Person类就是一个典型的“低内聚”的类,很容易发生改变。比如说,现在老师要求学生也要考试,则Person类需要新增一个方法:test,如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.cohesion.low;  
  2.   
  3. /** 
  4.  * “人”的类设计 
  5.  * 
  6.  */  
  7. public class Person {  
  8.   
  9.     /** 
  10.      * 学生的职责:学习 
  11.      */  
  12.     public void study() {  
  13.         //TODO: student's responsibility  
  14.     }  
  15.       
  16.     /** 
  17.      * 学生的职责:考试 
  18.      */  
  19.     public void test(){  
  20.         //TODO: student's responsibility  
  21.     }  
  22.       
  23.     /** 
  24.      * 运动员的职责:运动 
  25.      */  
  26.     public void play(){  
  27.         //TODO: sportsman's responsibility  
  28.     }  
  29.       
  30.     /** 
  31.      * 演员的职责:扮演 
  32.      */  
  33.     public void act(){  
  34.         //TODO: actor's responsibity  
  35.     }  
  36. }  

由于Coach和Director类都依赖于Person类,Person类改变后,虽然这个改动和Coach、Director都没有关系,但Coach和Director类都需要重新编译测试部署(即使是PHP这样的脚本语言,至少也要测试)。

 

同样,CoachDirector也都可能增加其它对Person的要求,这样Person类就需要同时兼顾3个类的业务要求,且任何一个变化,TeacherCoachDirector都需要重新编译测试部署。

 

对于耦合,我们采用同样的方式进行分析,即:如果高耦合,将会怎样?

高耦合的情况下,模块依赖了大量的其它模块,这样任何一个其它依赖的模块变化,模块本身都需要受到影响。所以,高耦合的问题其实也是“不稳定”,当然,这个不稳定和低内聚不完全一样。对于高耦合的模块,可能本身并不需要修改,但每次其它模块修改,当前模块都要编译、测试、部署,工作量同样不小。

 

我们同样以一个样例来说明。假设我们要设计一个Boss类,Boss是要管整个公司的,那么我们假设这是一家“麻雀虽小五脏俱全”的公司,同时有“研发、测试、技术支持、销售、会计、行政”等部门。

Boss.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.coupling.high;  
  2.   
  3. /** 
  4.  * “老板”类 
  5.  * 
  6.  */  
  7. public class Boss {  
  8.   
  9.     private Tester tester;  
  10.     private Developer developer;  
  11.     private Supporter supporter;  
  12.     private Administration admin;  
  13.     private Accountant accountant;  
  14.       
  15.     /** 
  16.      * Boss每天检查工作,看到下面的代码,是否觉得Boss很忙? 
  17.      */  
  18.     public void check(){  
  19.           
  20.         //检查测试工作  
  21.         tester.report();  
  22.           
  23.         //检查研发工作  
  24.         developer.report();  
  25.           
  26.         //检查技术支持工作  
  27.         supporter.report();  
  28.           
  29.         //检查行政工作  
  30.         admin.report();  
  31.           
  32.         //检查财务工作  
  33.         accountant.report();  
  34.     }  
  35. }  

Accountant.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.coupling.high;  
  2.   
  3. /** 
  4.  * “财务”类 
  5.  * 
  6.  */  
  7. public class Accountant {  
  8.   
  9.     public void report(){  
  10.         System.out.print("Accountant report");  
  11.     }  
  12. }  

Administration.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.coupling.high;  
  2.   
  3. /** 
  4.  * “行政”类  
  5.  * 
  6.  */  
  7. public class Administration {  
  8.   
  9.     public void report(){  
  10.         System.out.print("Administration report");  
  11.     }  
  12. }  

Developer.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.coupling.high;  
  2.   
  3. /** 
  4.  * “开发”类 
  5.  * @author Administrator 
  6.  * 
  7.  */  
  8. public class Developer {  
  9.   
  10.     public void report(){  
  11.         System.out.print("Developer report");  
  12.     }  
  13. }  

Supporter.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.coupling.high;  
  2.   
  3. /** 
  4.  * “技术支持”类 
  5.  * 
  6.  */  
  7. public class Supporter {  
  8.   
  9.     public void report(){  
  10.         System.out.print("Supporter report");  
  11.     }  
  12. }  

Tester.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.coupling.high;  
  2.   
  3. /** 
  4.  * “测试”类 
  5.  * 
  6.  */  
  7. public class Tester {  
  8.   
  9.     public void report(){  
  10.         System.out.print("Tester report");  
  11.     }  
  12. }  


好吧,Boss很忙,我们也很钦佩,但是有一天,研发的同学觉得他们应该做更多的事情,于是他们增加了一个“研究”的工作,且这个工作也不需要报告给Boss,例如:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.oo.coupling.high;  
  2.   
  3. /** 
  4.  * “开发”类 
  5.  * @author Administrator 
  6.  * 
  7.  */  
  8. public class Developer {  
  9.   
  10.     public void report(){  
  11.         System.out.print("Developer report");  
  12.     }  
  13.       
  14.     /** 
  15.      * 研发新增加一个研究的任务,这个任务也不需要向Boss汇报 
  16.      */  
  17.     public void research(){  
  18.         System.out.print("Developer is researching big data, hadoop :)");  
  19.     }  
  20. }  

虽然这个工作不需要报告给Boss,但由于Developer类修改了,那么Boss类就需要重新编译测试部署。其它几个Tester、Supporter类等也是类似,一旦改变,即使这些类和Boss类半毛钱关系都没有,Boss类还是需要重新编译测试部署,所以Boss类是很不稳定的。

 

所以,无论是“低内聚”,还是“高耦合”,其本质都是“不稳定”,不稳定就会带来工作量,带来风险,这当然不是我们希望看到的,所以我们应该做到“高内聚低耦合”。

 

回答完第一个问题后,我们来看第二个问题:高内聚低耦合是否意味着内聚越高越好,耦合越低越好?

按照我们前面的解释,内聚越高,一个类越稳定;耦合越低,一个类也很稳定,所以当然是内聚越高越好,耦合越低越好了。

但其实稍有经验的同学都会知道这个结论是错误的,并不是内聚越高越好,耦合越低越好,真正好的设计是在高内聚和低耦合间进行平衡,也就是说高内聚和低耦合是冲突的

 

我们详细来分析一下为什么高内聚和低耦合是冲突的。

对于内聚来说,最强的内聚莫过于一个类只写一个函数,这样内聚性绝对是最高的。但这会带来一个明显的问题:类的数量急剧增多,这样就导致了其它类的耦合特别多,于是整个设计就变成了“高内聚高耦合”了。由于高耦合,整个系统变动同样非常频繁。

 

同理,对于耦合来说,最弱的耦合是一个类将所有的函数都包含了,这样类完全不依赖其它类,耦合性是最低的。但这样会带来一个明显的问题:内聚性很低,于是整个设计就变成了“低耦合低内聚”了。由于低内聚,整个类的变动同样非常频繁。

对于“低耦合低内聚”来说,还有另外一个明显的问题:几乎无法被其它类重用。原因很简单,类本身太庞大了,要么实现很复杂,要么数据很大,其它类无法明确该如何重用这个类。

 

所以,内聚和耦合的两个属性,排列组合一下,只有“高内聚低耦合”才是最优的设计

因此,在实践中我们需要牢牢记住需要在高内聚和低耦合间进行平衡,而不能走极端。 具体如何平衡,且听下回分解。


================================================ 
转载请注明出处:http://blog.csdn.net/yunhua_lee/article/details/25074707
================================================ 


相关文章
|
搜索推荐 Java 数据库
基于SpringBoot校园二手书交易管理系统
基于SpringBoot校园二手书交易管理系统
|
存储 JSON 前端开发
JSON数组的概念、语法和用法
JSON数组的概念、语法和用法
1755 3
|
消息中间件 微服务
MQTT X连接阿里云微服务消息队列MQTT
MQTT X 是 EMQ 开源的一款优雅的跨平台 MQTT 5.0 桌面客户端,它支持 macOS, Linux, Windows。MQTT X 的 UI 采用了聊天界面形式,简化了页面操作逻辑,用户可以快速创建连接,允许保存多个客户端,方便用户快速测试 MQTT/MQTTS 连接,及 MQTT 消息的订阅和发布。本文演示如何使用该工具快速连接阿里云微服务消息队列MQTT,并进行消息的发布和订阅测试。
4519 0
MQTT X连接阿里云微服务消息队列MQTT
|
JSON NoSQL MongoDB
MongoDB Compass的安装及使用图文说明(非常详细)
MongoDB Compass的安装及使用图文说明(非常详细)
4274 2
|
传感器 物联网 人机交互
物联网:物联网,作为新一代信息技术的重要组成部分,通过智能感知、识别技术与普适计算等通信感知技术,将各种信息传感设备与互联网结合起来而形成的一个巨大网络,实现了物物相连、人物相连,开启了万物互联的新时代。
在21世纪,物联网(IoT)作为新一代信息技术的核心,正以前所未有的速度重塑生活、工作和社会结构。本文首先介绍了物联网的概念及其在各领域的广泛应用,强调其技术融合性、广泛的应用范围以及数据驱动的特点。接着,详细阐述了物联网行业的现状和发展趋势,包括政策支持、关键技术突破和应用场景深化。此外,还探讨了物联网面临的挑战与机遇,并展望了其未来在技术创新和模式创新方面的潜力。物联网行业正以其独特魅力引领科技发展潮流,有望成为推动全球经济发展的新引擎。
|
8月前
|
SQL JSON 数据可视化
基于 DIFY 的自动化数据分析实战
本文介绍如何使用DIFY搭建数据分析自动化流程,实现从输入需求到查询数据库、LLM分析再到可视化输出的全流程。基于经典的employees数据集和DIFY云端环境,通过LLM-SQL解析、SQL执行、LLM数据分析及ECharts可视化等模块,高效完成数据分析任务。此方案适用于人力资源分析、薪酬管理等数据密集型业务,显著提升效率并降低成本。
12323 16
|
关系型数据库 MySQL Go
golang使用gorm操作mysql1
golang使用gorm操作mysql1
golang使用gorm操作mysql1
|
Kubernetes 开发工具 容器
k8s搭建集群—部署CNI网络插件——2023.02
k8s搭建集群—部署CNI网络插件——2023.02
755 0
|
存储 缓存 JSON
详解HTTP四种请求:POST、GET、DELETE、PUT
【4月更文挑战第3天】
66746 4
详解HTTP四种请求:POST、GET、DELETE、PUT
|
存储 SQL 弹性计算
JVM时区配置-两行代码让我们一帮子人熬了一个通宵
不经意的两行代码让我们一帮子人熬了一个通宵
25921 10