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

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

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

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

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

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

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

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

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

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

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

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

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

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

Person.java

package com.oo.cohesion.low;

/**
 * “人”的类设计
 *
 */
public class Person {

	/**
	 * 学生的职责:学习
	 */
	public void study() {
		//TODO: student‘s responsibility
	}

	/**
	 * 运动员的职责:运动
	 */
	public void play(){
		//TODO: sportsman‘s responsibility
	}

	/**
	 * 演员的职责:扮演
	 */
	public void act(){
		//TODO: actor‘s responsibity
	}
}

Teacher.java

package com.oo.cohesion.low;

/**
 * “老师”的类设计
 *
 */
public class Teacher {

	public void teach(Person student){
		student.study(); //依赖Person类的“学生”相关的职责
	}
}

Coach.java

package com.oo.cohesion.low;

/**
 * “教练”的类设计
 *
 */
public class Coach {

	public void train(Person trainee){
		trainee.play();  //依赖Person类的“运动员”职责
	}
}

Director.java

package com.oo.cohesion.low;

/**
 * “导演”的类设计
 *
 */

public class Director {

	public void direct(Person actor){
		actor.act(); //依赖Person类“演员”的相关职责
	}
}

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

package com.oo.cohesion.low;

/**
 * “人”的类设计
 *
 */
public class Person {

	/**
	 * 学生的职责:学习
	 */
	public void study() {
		//TODO: student‘s responsibility
	}

	/**
	 * 学生的职责:考试
	 */
	public void test(){
		//TODO: student‘s responsibility
	}

	/**
	 * 运动员的职责:运动
	 */
	public void play(){
		//TODO: sportsman‘s responsibility
	}

	/**
	 * 演员的职责:扮演
	 */
	public void act(){
		//TODO: actor‘s responsibity
	}
}

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

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

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

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

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

Boss.java

package com.oo.coupling.high;

/**
 * “老板”类
 *
 */
public class Boss {

	private Tester tester;
	private Developer developer;
	private Supporter supporter;
	private Administration admin;
	private Accountant accountant;

	/**
	 * Boss每天检查工作,看到下面的代码,是否觉得Boss很忙?
	 */
	public void check(){

		//检查测试工作
		tester.report();

		//检查研发工作
		developer.report();

		//检查技术支持工作
		supporter.report();

		//检查行政工作
		admin.report();

		//检查财务工作
		accountant.report();
	}
}

Accountant.java

package com.oo.coupling.high;

/**
 * “财务”类
 *
 */
public class Accountant {

	public void report(){
		System.out.print("Accountant report");
	}
}

Administration.java

package com.oo.coupling.high;

/**
 * “行政”类
 *
 */
public class Administration {

	public void report(){
		System.out.print("Administration report");
	}
}

Developer.java

package com.oo.coupling.high;

/**
 * “开发”类
 * @author Administrator
 *
 */
public class Developer {

	public void report(){
		System.out.print("Developer report");
	}
}

Supporter.java

package com.oo.coupling.high;

/**
 * “技术支持”类
 *
 */
public class Supporter {

	public void report(){
		System.out.print("Supporter report");
	}
}

Tester.java

package com.oo.coupling.high;

/**
 * “测试”类
 *
 */
public class Tester {

	public void report(){
		System.out.print("Tester report");
	}
}

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

package com.oo.coupling.high;

/**
 * “开发”类
 * @author Administrator
 *
 */
public class Developer {

	public void report(){
		System.out.print("Developer report");
	}

	/**
	 * 研发新增加一个研究的任务,这个任务也不需要向Boss汇报
	 */
	public void research(){
		System.out.print("Developer is researching big data, hadoop :)");
	}
}

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

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

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

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

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

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

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

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

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

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

因此,在实践中我们需要牢牢记住需要在高内聚和低耦合间进行平衡,而不能走极端。而如何平衡,其实就体现了设计师的经验和水平。

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

时间: 2024-10-12 20:10:44

连载:面向对象葵花宝典:思想、技巧与实践(29) - 高内聚低耦合的相关文章

浅谈面向对象开发原则:高内聚,低耦合

软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准.划分摸块的一个准则就是高内聚低耦合. 这是软件工程中的概念,是判断设计好坏的标准,主要是面向OO的设计,主要是看类的内聚性是否高,偶合度是否低. 每一个类完成特定的独立的功能,这个就是高内聚.耦合就是类之间的互相调用关系,如果耦合很强,互相牵扯调用很多,那么会牵一发而动全身,不利于维护和扩展. 类之间的设置应该要低耦合,但是每个类应该要高内聚.耦合是类之间相互依赖的尺度.如果每个对象都有引用其它所有的对象,那么就有高耦合,这是不合乎要求的

以MyBatis+SpringMVC3.0实现的,借鉴了Hibernate设计思想,采用封装、抽象、继承的设计思想,做到了数据与相应的操作的高内聚低耦合的实现

QQ:1138789752 Email.java类: package com.lmc.ink.entity.seo; import com.lmc.ink.entity.IEntity; /** * 推广邮箱表(s_email)实体类 * @author lmc * */ public class Email extends IEntity { /** * 唯一标识 * */ public static final long serialVersionUID = 537132705L; /**

设计思想之高内聚低耦合

1.什么是高内聚? 内聚针对的是模块内部关系,指的是模块各构成要素间的联系,高内聚说明各构成要素间联系紧密. 2.高内聚的优点 各构成要素间联系紧密,说明各构成要素是实现模块功能充分的存在,没有某一个要素是实现功能不需要的,各要素都被充分地利用了起来,不仅使代码简洁,而且便于维护与复用.试想,假如某一部分对于功能而言有时需要,有时不需要,那么修改模块功能时就需要对情况作出判断,使修改变得复杂. 高内聚的一个重要标志是模块功能单一.假如一个模块实现了两个功能,那么实现这两个功能的代码间的联系必定是

连载:面向对象葵花宝典:思想、技巧与实践(30) - SRP原则

前面具体阐述了"高内聚低耦合"的整体设计原则.但怎样让设计满足这个原则,并非一件简单的事情.幸好各位前辈和大牛已经帮我们归纳总结出来了,这就是"设计原则"和"设计模式".毫不夸张的说,仅仅要你吃透这些原则和模式并熟练应用,就能够做出非常好的设计. ================================================================== [SRP原则具体解释] SRP.single responsibil

《面向对象葵花宝典》阅读笔记

满满的干货!推荐大家购买的一本书,里面很多的内容,都是我编程过程经历过的困惑(相信大家都会遇到),如果早点看到这本书,相信当时我也不会困惑那么久了~所以记录总结一下. PS.欲看此书,不必自宫…… 面向对象理论面向过程与面向对象为什么要面向对象类对象接口抽象类抽象封装继承多态需求模型需求分析518方法,我要发~用例方法要画UML图吗?功能提取领域模型设计模型类模型动态模型实现模型设计原则内聚耦合高内聚低耦合设计模式面向对象架构设计业务架构领域架构软件建构面向对象架构设计技巧原则拆与合 面向对象理

面向对象的思想 泛型和委托

面向对象是一种思想,他的技术体现,可以体现在代码中使用泛型,方法与方法之间的调用不是直接调用,而是使用委托. 使用了泛型,就可以不用考虑数据的类型,从而做到统一的处理.就比如我们以前写代码,每个实体类对应一个DAL,然后每个DAL里面都有增删改查的方法,每个方法都是针对固定的类型来做的.如Update实体A,则一定只能更新实体A.但是使用了泛型就可以Update(<T>),这个T是什么类型,只要你指定什么类型,他就是什么类型,T是实体A,就更新实体A,T是实体B就更新实体B. 只要类型不确定,

面向对象编程思想(OOP)

本文我将从面向对象编程思想是如何解决软件开发中各种疑难问题的角度,来讲述我们面向对象编程思想的理解,梳理面向对象四大基本特性.七大设计原则和23种设计模式之间的关系. 软件开发中疑难问题: 软件复杂庞大 很多软件进入维护阶段 需求的不断变更 软件开发中存在很多其他的问题,上面只是从程序开发和设计的角度看到的部分问题.需求解决上面软件开发中的问题,就要求我们编写(设计)的软件具有很好的可读性.可维护性和可扩展性.我们需要保证代码具有高内聚低耦合. 下面将简单介绍面向对象的一些基本特性.设计原则,以

面向对象编程思想(OOP)

本文我将从面向对象编程思想是如何解决软件开发中各种疑难问题的角度,来讲述我们面向对象编程思想的理解,梳理面向对象四大基本特性.七大设计原则和23种设计模式之间的关系. 软件开发中疑难问题: 软件复杂庞大 很多软件进入维护阶段 需求的不断变更 软件开发中存在很多其他的问题,上面只是从程序开发和设计的角度看到的部分问题.需求解决上面软件开发中的问题,就要求我们编写(设计)的软件具有很好的可读性.可维护性和可扩展性.我们需要保证代码具有高内聚低耦合. 下面将简单介绍面向对象的一些基本特性.设计原则,以

面向对象的思想 泛型和托付

面向对象是一种思想,他的技术体现,能够体如今代码中使用泛型,方法与方法之间的调用不是直接调用,而是使用托付. 使用了泛型,就能够不用考虑数据的类型,从而做到统一的处理.就比方我们曾经写代码,每一个实体类相应一个DAL,然后每一个DAL里面都有增删改查的方法,每一个方法都是针对固定的类型来做的.如Update实体A,则一定仅仅能更新实体A.可是使用了泛型就能够Update(<T>),这个T是什么类型,仅仅要你指定什么类型,他就是什么类型,T是实体A,就更新实体A,T是实体B就更新实体B. 仅仅要