内聚和耦合
任务描述
本关考察内聚与耦合知识点。为了完成本关任务,你需要掌握:
- 模块与模块化;
- 内聚与耦合的定义;
- 常见的耦合与内聚类型。
相关知识
模块
模块是由边界元素限定的相邻程序元素(例如:数据说明,可执行的语句)的序列,而且有一个总体标识符代表它。模块是构成程序的基本构件。
面向对象方法学中的对象,对象内的方法,过程、函数、子程序和宏等,都可作为模块。
模块化
模块化是指把程序划分成独立命名且可独立访问的模块,每个模块完成一个子功能,把这些模块集成起来构成一个整体,可以完成指定的功能满足用户的需求。
把复杂的问题分解成许多容易解决的小问题,原来的问题也就容易解决了。
耦合定义
不同模块之间相互依赖程度的度量。
常见耦合类型
内容耦合
内容耦合指一个模块直接修改或操作另一个模块的数据,或者一个模块直接转到另一个模块的内部,或者一个模块有多个入口。
示例如图1及下述代码。这份代码中的类A与类B间调用并修改了相互的变量,这是不允许的。正确写法应是将m与n变量修改为私有 private 。
图1 内容耦合示例
class A {
public int m;
void method(){
B b = new B();
b.n += 1;
}
}
class B{
public int n;
void method(){
A a = new A();
a.m += 1;
}
}
控制耦合
控制耦合指一个模块向另一模块传递一个控制信号,接收信号的模块将依据该信号值进行必要的活动。这种耦合的实质是在单一接口上选择多功能模块中的某项功能。因此,对所控制模块的任何修改,都会影响控制模块。
模块之间传递的不是数据信息,而是控制信息例如标志、开关量等,一个模块控制了另一个模块的功能。
控制耦合具体示例如图2及下述代码。这份代码中的 output() 方法通过参数 flag 来控制执行的具体功能。output() 方法与 main() 方法之间即为控制耦合。
图2 控制耦合示例
void output(boolean flag) {
if(flag) {
System.out.println(“YES!”);
}
else {
System.out.println(“NO!”);
}
}
void main() {
boolean flag;
…
output(flag);
}
数据耦合
模块间通过参数(不是控制参数、公共数据结构或外部变量)来传递基本类型的数据,称为数据耦合。
由于限制了只通过参数表传递数据,按数据耦合开发的程序界面简单、安全可靠。因此,数据耦合是松散的耦合,模块之间的独立性比较强。在软件程序结构中至少必须有这类耦合。
数据耦合具体示例如图3及下述代码。sum() 方法计算两个数的和,main() 方法向 sum() 方法传递了x,y两个整型基本类型参数。sum() 与main() 之间即为数据耦合关系。
图3 数据耦合示例
//模块A
int sum(int a, int b){
int c;
c = a + b;
return c;
}
//模块B
void main(){
int x = 5;
int y = 10;
int z = sum(x, y);
System.out.println(“x+y=%d”, z);
}
内聚定义
一个模块之内各成分之间相互依赖程度的度量。
常见内聚类型
顺序内聚
若一个模块中的各个部分都与同一个功能密切相关,并且必须按照先后顺序执行(通常前一个部分的输出数据就是后一个部分的输入数据),则称该模块的内聚为顺序内聚。
具体实例见下述代码。其中 getRetireYear() 方法根据员工生日计算员工年龄,再根据员工年龄计算退休日期,该模块具有顺序内聚。
void getRetireYear(){
int birthYear = 0;
int age = 0;
Scan scanner = new Scan(System.in);
//得出年龄
System.out.println(“请输入出生年份”);
birthYear = scanner.nextInt();
int curYear = getYear();//获取当前年份
age = curYear – birthYear;
System.out.println(“得出您的年龄为:%d”,age);
//得出退休时间
int retireYear = 0;
retireYear = curYear + 60 – age;
System.out.println(“得出您的退休年份为:%d”, retireYear);
}
功能内聚
功能内聚指模块各个成分结合在一起,完成一个特定的功能。功能性模块具有内聚性最强、与其他模块联系少的特点。
具体实例见下述代码。其中 getAge() 方法根据员工生日计算员工年龄,该模块具有功能内聚。
void getAge(){
int birthYear = 0;
int age = 0;
Scan scanner = new Scan(System.in);
//得出年龄
System.out.println(“请输入出生年份”);
birthYear = scanner.nextInt();
int curYear = getYear();//获取当前年份
age = curYear – birthYear;
System.out.println(“得出您的年龄为:%d”,age);
}
闯关要求
案例介绍
网上商城系统致力于提供产品展示及订购,为核心的网上购物服务宣传自己商店的商品并将自己的产品展现给用户,让客户通过网站能自由地选择购买产品。
用户可以在商城前台进行浏览商品,加入购物车,下单等操作。后台管理模块可以对商品、订单、会员以及库存进行管理。
-
1、
在网上商城系统中,假设有一个商品类为Product,该类有一个记录商品价格的变量unitPrice,Order类为订单类,代码如下所示。分析该代码,指出存在的耦合。
public class Product{
public float unitPrice;
...;
}
public class Order{
private Product product = new Product();
public void setItem(){
product.unitPrice = -100.0;
}
...;
}
内容耦合
B、控制耦合
C、数据耦合
D、公共耦合
-
2、
网上商城系统中用户会分为普通用户和会员用户。模块A获取用户类型(普通用户、会员用户)传递给模块B,模块B根据不同类型的用户提供不同的服务。这种情况下模块A和模块B之间存在以下哪种耦合?
A、内容耦合
B、数据耦合
C、控制耦合
D、标记耦合
-
3、
在网上商城系统中,有模块A“生成订单信息”以及模块B“计算价格”。根据下列描述,判断A和B之间存在哪一种耦合。
模块A “生成订单信息”:OrderInfo genOrderInfo(){
//OrderInfo为“订单信息”类
OrderInfo orderInfo = new OrderInfo();
…;
price = calc_price(number, unitPrice);
orderInfo.set(price);
…;
return orderInfo;
}
模块B“计算价格”:
float calc_price(int number, float unitPrice){
float price;
…;
return price;
}
控制耦合
B、数据耦合
C、内容耦合
D、公共耦合
-
4、
在网上商城系统中,有统计食品是否过期的功能:商城管理员打开物品信息文件,读出文件中每件食品的生产日期与保质期,判断该食品是否已经过期,将结论写入物品信息文件中;之后再读取该文件,对已经标记为过期的食品做下架处理。
A、
上述描述中,存在的内聚为:顺序内聚
B、逻辑内聚
C、过程内聚
D、通信内聚
-
5、
若将第四题中的下架过期食品模块分解为两个子模块:判断是否过期模块以及下架模块。判断是否过期模块根据每件食品的生产日期与保质期,来判断该食品是否已经过期;下架模块对已经标记为过期的食品做下架处理。在模块分解后,我们将顺序内聚变成了以下哪种内聚?
A、逻辑内聚
B、过程内聚
C、通信内聚
D、功能内聚