组合优于继承:面向对象原则、内聚、耦合随感

这些设计的基本目标都是为了达到易理解、易修改、易复用。

基本上,职责会发生改变的时候需要使用组合。继承固然实现了高度复用,随之而来的是面对变更时的不灵活。组合可以更灵活地实现职责的分配。

违反LSP的时候也需要组合,用来复用逻辑上的“父类”的代码。在这种情况下,只需要复用“父类”几个方法的代码,但不需要实现其他方法。因为在这个场景下,继承不适用(违反了LSP),就需要分离出一个接口,然后进行组合;这也就变成了策略模式。

也就是说:

A. 类A的某个职责有Type1……Typen多种情况,而且会实时根据需要从typei种类型变化为typej种类型;
B. 类A需要复用类B几个方法的代码,同时不需要实现B的其他方法;
C. 类A的某个职责有Type1……Typen多种情况,而且会同时处于其中的多种情景。

当然,不要滥用“组合优于继承”,能用继承的时候还是要用继承的。继承有更好的语义和更清晰的结构。

接口分离ISP主要是为了避免依赖不需要的接口,而产生不必要的访问耦合;依赖倒置DIP主要是为了应对变更,两者还是有差别的。

有数据的人负责执行创建。所谓的组合和聚合关系,从本质上说,也是进行组合的对象持有的数据多。这是GRASP里的创建者模式。这是一个动态的协作过程。

数据和行为要一致,且数据和行为要集中。这是GRASP里的信息专家模式:把职责分配给具有完成该职责所需信息的那个类。这是一个静态的职责分配过程。

适当添加辅助类来进行职责分配。比如,持久化数据,进行界面交互,这就是GRASP里的纯虚构和控制器模式了。因为这些并不是真实世界(问题域)的映射,但是是真实存在的职责,就需要一些虚构的东西去承载它。其实还是详细设计的静态模型——职责的分配。

我甚至可以说,对象即职责,一个对象就是一个职责。因此,一个对象只应该有一个变化的原因,也就是SRP了。

产生控制耦合的根本原因是职责分配不均。我与其传递控制标志(flag)给另一个对象来实现不同的行为,不如想想怎么通过组合来实现这种行为的变化;状态模式和策略模式都是不错的选择。相对于数据,控制信息是无意义的。

访问public的成员变量和内部类会导致内容耦合,goto语句也会。因为这相当于直接访问了别的模块的内部,封装完全失效。直接用编译后的字节码之类的覆盖也算,不过这也太……

公共耦合不仅来自全局变量(共用的变量),还可能来自共用的文件和数据,甚至是公共的设备;这也算是公共耦合了。只要是公共的环境,就是公共耦合。

印记耦合有时候很微妙。只要没有将传递的属性全部使用,就算印记耦合,哪怕是一些无关紧要的ID。而且,印记耦合必然发生在复杂的数据结构上。简单的结构无论如何都不会产生印记耦合的。

控制耦合基本来自switch和连续的if-else。但本质上是传递了无意义的控制信息。如果switch传递的是有用的数据,那也不算是控制耦合。

同样的数据叫通信内聚,同样的过程叫过程内聚,那同样的数据+同样的过程呢?顺序内聚。也就是上一个是输出是下一个的输入,是一个完整的流程。

功能内聚挺玄乎的,是整个模块为了实现一个功能,而且必须只是一个功能。完成订单整个功能可以是功能内聚,但是你说是不是同样的数据同样的过程呢?是不是顺序内聚呢?但是功能内聚更高一点。

只要使用了同一份数据,哪怕看起来风马牛不相及的东西,也可以构成通信内聚。比如,生成报告,保存报告,打印报告三个功能,看起来其实没什么必然联系,甚至感觉像是逻辑内聚,但因为都操作了报告的数据,所以就是通信内聚。

初始化是时间内聚。只要是初始化就是,这一条优先级很高。

一段代码不一定只有一种可能性。在不同的context下,可能会表现出不同的耦合性和内聚性。

内聚服从于耦合。高内聚不一定低耦合,有可能是高耦合。高内聚高耦合、低内聚低耦合是两种经常犯的错误。

总结一下:

如果是初始化,或者是同时执行=>时间内聚

传递控制信息的switch和连续的if-else,也就是说,根据控制信号执行不同的行为=>逻辑内聚(因为其实是根据标志做完全不同的事,实现不同的功能;如果只是单独传递一下控制信号,并不算逻辑内聚)

数据结构、ADT=>信息内聚

确定是同一个功能=>功能内聚

不能确定是不是一个功能,或者已经确定不是一个功能的情况下:

如果操作同一份数据且有顺序=>顺序内聚(通信+过程);虽然优先级高,但很模糊,需要慎重

如果操作同一份数据但没有顺序=>通信内聚

如果不止一份数据,但是有顺序(同一个问题)=>过程内聚

毫无关联=>偶然内聚

需要强调的是,上面提到的数据,不是简单的变量;一般是成员变量,或者是全局共享的属性。全局变量带来的公共耦合不在这次的讨论范围内。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值